<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Spring Data Redis - Spring Data Redis | Docs4dev</title>
<meta charset="UTF-8">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="Spring Data Redis, part of the larger Spring Data family, provides easy configuration and access to Redis from Spring applications. It offers both low-level and high-level abstractions for interacting with the store, freeing the user from infrastructural concerns.">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="HandheldFriendly" content="true">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta property="og:type" content="website">
<meta property="og:title" content="Spring Data Redis - Spring Data Redis">
<meta property="og:url" content="https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/">
<meta property="og:site_name" content="Docs4dev">
<meta property="og:locale" content="zh_CN">
<meta name="twitter:card" content="Spring Data Redis, part of the larger Spring Data family, provides easy configuration and access to Redis from Spring applications. It offers both low-level and high-level abstractions for interacting with the store, freeing the user from infrastructural concerns.">
<meta name="generator" content="Docs4dev template engine">
<link rel="stylesheet" href="static/css/app.min.css">
<link rel="shortcut icon" href="https://www.docs4dev.com/static/images/favicon.ico" type="image/x-icon">
<script async="" src="static/js/js.js"></script>
<script async="" src="static/js/adsbygoogle.js" crossorigin="anonymous"></script>
<script>
    window.dataLayer = window.dataLayer || [];

    function gtag() {
      dataLayer.push(arguments);
    }

    gtag('js', new Date());
    gtag('config', 'UA-129571937-1');
  </script>
<link rel="amphtml" href="https://www.docs4dev.com/amp/docs/en/spring-data-redis/1.8.18.RELEASE/reference/index.html">

<script type="application/ld+json">{"name":null,"headline":"Spring Data Redis-Spring Data Redis","inLanguage":"en-US","version":"1.8.18.RELEASE","image":"/static/icon/icon-spring-data.png","datePublished":"2021-05-20T12:51:01Z","dateCreated":"2021-05-20T12:51:01Z","dateModified":"2021-07-03T12:24:35Z","@context":"https://schema.org/","@type":"APIReference","abstract":"Spring Data Redis, part of the larger Spring Data family, provides easy configuration and access to Redis from Spring applications. It offers both low-level and high-level abstractions for interacting with the store, freeing the user from infrastructural concerns."}</script>
</head>
<body>
<div class="book with-summary">
<div class="book-summary">
<div class="logo">
<a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference');" style="color: inherit;">
<img src="static/picture/icon-spring-data.png" style="width: 48px; height: 48px;" alt="Logo">
</a>
<b style="color: inherit; margin-left: 8px;">Spring Data Redis</b>
</div>
<div class="item">
<div>
<label for="version">版本</label>
<select id="version" onchange="onVersionChange(this)">
<option value="1.8.18.RELEASE" selected="selected">1.8.18.RELEASE</option>
<option value="2.1.5.RELEASE">2.1.5.RELEASE</option>
</select>
</div>
<div style="margin-top: 8px;">
<label for="language">语言</label>
<select id="language" onchange="onLangChange(this)" value="en">
<option value="en" selected="selected">English</option>
<option value="zh">中文</option>
</select>
</div>

</div>
<div class="item menus">
<a title="Table of Contents" style="margin-right: 8px;" href="#">
<i class="fa fa-chevron-left"></i>
<span style="margin-left: 2px;">返回目录</span>
</a>
</div>
<nav role="navigation" id="navigation">
<ul class="summary">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_preface');" title="Preface"> Preface </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#new-features');" title="1. New Features"> 1. New Features </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#new-in-1.8.0');" title="1.1. New in Spring Data Redis 1.8"> 1.1. New in Spring Data Redis 1.8 </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#new-in-1.7.0');" title="1.2. New in Spring Data Redis 1.7"> 1.2. New in Spring Data Redis 1.7 </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#new-in-1-6-0');" title="1.3. New in Spring Data Redis 1.6"> 1.3. New in Spring Data Redis 1.6 </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#new-in-1-5-0');" title="1.4. New in Spring Data Redis 1.5"> 1.4. New in Spring Data Redis 1.5 </a> </li>
</ul> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#introduction');" title="Introduction"> Introduction </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#why-spring-redis');" title="2. Why Spring Data Redis?"> 2. Why Spring Data Redis? </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#requirements');" title="3. Requirements"> 3. Requirements </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started');" title="4. Getting Started"> 4. Getting Started </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:first-steps');" title="4.1. First Steps"> 4.1. First Steps </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:first-steps:spring');" title="4.1.1. Learning Spring"> 4.1.1. Learning Spring </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:first-steps:nosql');" title="4.1.2. Learning NoSQL and Key Value Stores"> 4.1.2. Learning NoSQL and Key Value Stores </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:first-steps:samples');" title="4.1.3. Trying out the Samples"> 4.1.3. Trying out the Samples </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:help');" title="4.2. Need Help?"> 4.2. Need Help? </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:help:community');" title="4.2.1. Community Support"> 4.2.1. Community Support </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:help:professional');" title="4.2.2. Professional Support"> 4.2.2. Professional Support </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#get-started:up-to-date');" title="4.3. Following Development"> 4.3. Following Development </a> </li>
</ul> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#reference');" title="Reference Documentation"> Reference Documentation </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis');" title="5. Redis support"> 5. Redis support </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:requirements');" title="5.1. Redis Requirements"> 5.1. Redis Requirements </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:architecture');" title="5.2. Redis Support High-level View"> 5.2. Redis Support High-level View </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:connectors');" title="5.3. Connecting to Redis"> 5.3. Connecting to Redis </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:connectors:connection');" title="5.3.1. RedisConnection and RedisConnectionFactory"> 5.3.1. RedisConnection and RedisConnectionFactory </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:connectors:jedis');" title="5.3.2. Configuring the Jedis Connector"> 5.3.2. Configuring the Jedis Connector </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:connectors:jredis');" title="5.3.3. Configuring JRedis connector (Deprecated since 1.7)"> 5.3.3. Configuring JRedis connector (Deprecated since 1.7) </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:connectors:srp');" title="5.3.4. Configuring SRP connector (Deprecated since 1.7)"> 5.3.4. Configuring SRP connector (Deprecated since 1.7) </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:connectors:lettuce');" title="5.3.5. Configuring the Lettuce connector"> 5.3.5. Configuring the Lettuce connector </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:sentinel');" title="5.4. Redis Sentinel Support"> 5.4. Redis Sentinel Support </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:template');" title="5.5. Working with Objects through RedisTemplate"> 5.5. Working with Objects through RedisTemplate </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:string');" title="5.6. String-focused Convenience Classes"> 5.6. String-focused Convenience Classes </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:serializer');" title="5.7. Serializers"> 5.7. Serializers </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.hashmappers.root');" title="5.8. Hash mapping"> 5.8. Hash mapping </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_hash_mappers');" title="5.8.1. Hash Mappers"> 5.8.1. Hash Mappers </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.hashmappers.jackson2');" title="5.8.2. Jackson2HashMapper"> 5.8.2. Jackson2HashMapper </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#pubsub');" title="5.9. Redis Messaging (Pub/Sub)"> 5.9. Redis Messaging (Pub/Sub) </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:pubsub:publish');" title="5.9.1. Publishing (Sending Messages)"> 5.9.1. Publishing (Sending Messages) </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:pubsub:subscribe');" title="5.9.2. Subscribing (Receiving Messages)"> 5.9.2. Subscribing (Receiving Messages) </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#tx');" title="5.10. Redis Transactions"> 5.10. Redis Transactions </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#tx.spring');" title="5.10.1. @Transactional Support"> 5.10.1. @Transactional Support </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#pipeline');" title="5.11. Pipelining"> 5.11. Pipelining </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#scripting');" title="5.12. Redis Scripting"> 5.12. Redis Scripting </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:support');" title="5.13. Support Classes"> 5.13. Support Classes </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis:support:cache-abstraction');" title="5.13.1. Support for the Spring Cache Abstraction"> 5.13.1. Support for the Spring Cache Abstraction </a> </li>
</ul> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#cluster');" title="6. Redis Cluster"> 6. Redis Cluster </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_enabling_redis_cluster');" title="6.1. Enabling Redis Cluster"> 6.1. Enabling Redis Cluster </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_working_with_redis_cluster_connection');" title="6.2. Working With Redis Cluster Connection"> 6.2. Working With Redis Cluster Connection </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_working_with_code_redistemplate_code_and_code_clusteroperations_code');" title="6.3. Working with RedisTemplate and ClusterOperations"> 6.3. Working with RedisTemplate and ClusterOperations </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories');" title="7. Redis Repositories"> 7. Redis Repositories </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.usage');" title="7.1. Usage"> 7.1. Usage </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.mapping');" title="7.2. Object-to-Hash Mapping"> 7.2. Object-to-Hash Mapping </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_customizing_type_mapping');" title="7.2.1. Customizing Type Mapping"> 7.2.1. Customizing Type Mapping </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.keyspaces');" title="7.3. Keyspaces"> 7.3. Keyspaces </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.indexes');" title="7.4. Secondary Indexes"> 7.4. Secondary Indexes </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.indexes.simple');" title="7.4.1. Simple Property Index"> 7.4.1. Simple Property Index </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.indexes.geospatial');" title="7.4.2. Geospatial Index"> 7.4.2. Geospatial Index </a> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.expirations');" title="7.5. Time To Live"> 7.5. Time To Live </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.references');" title="7.6. Persisting References"> 7.6. Persisting References </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.partial-updates');" title="7.7. Persisting Partial Updates"> 7.7. Persisting Partial Updates </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.queries');" title="7.8. Queries and Query Methods"> 7.8. Queries and Query Methods </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.cluster');" title="7.9. Redis Repositories Running on a Cluster"> 7.9. Redis Repositories Running on a Cluster </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#redis.repositories.cdi-integration');" title="7.10. CDI Integration"> 7.10. CDI Integration </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_redis_repositories_anatomy');" title="7.11. Redis Repositories Anatomy"> 7.11. Redis Repositories Anatomy </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_insert_new');" title="7.11.1. Insert new"> 7.11.1. Insert new </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_replace_existing');" title="7.11.2. Replace existing"> 7.11.2. Replace existing </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_save_geo_data');" title="7.11.3. Save Geo Data"> 7.11.3. Save Geo Data </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_find_using_simple_index');" title="7.11.4. Find using simple index"> 7.11.4. Find using simple index </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_find_using_geo_index');" title="7.11.5. Find using Geo Index"> 7.11.5. Find using Geo Index </a> </li>
</ul> </li>
</ul> </li>
</ul> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#appendixes');" title="Appendixes"> Appendixes </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#appendix:schema');" title="Appendix A: Schema"> Appendix A: Schema </a> </li>
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#appendix:command-reference');" title="Appendix B: Command Reference"> Appendix B: Command Reference </a>
<ul class="articles">
<li class="chapter"> <a href="javascript:window.open('https://www.docs4dev.com/docs/en/spring-data-redis/1.8.18.RELEASE/reference/all.html#_supported_commands');" title="Supported Commands"> Supported Commands </a> </li>
</ul> </li>
</ul> </li>
</ul>
</nav>
</div>
<div class="book-body">
<div class="body-inner">
<header class="book-header">
<div class="dropdown pull-right js-toolbar-action">
<a class="btn toggle-dropdown" aria-label="Language" href="#">
<i class="fa fa-globe"></i>
</a>
<div class="dropdown-menu dropdown-left">
<div class="dropdown-caret"><span class="caret-outer"></span><span class="caret-inner"></span></div>
<div class="buttons">
<button class="button size-1" onclick="changeLang('zh_CN')">中文</button>
</div>
<div class="buttons">
<button class="button size-1" onclick="changeLang('en_US')">English</button>
</div>
</div>
</div>
<a class="btn pull-right js-toolbar-action non-mobile" aria-label="Sign up" href="register.html">
<i class="fa fa-sign-in"></i> <span>注册</span>
</a>
<a class="btn pull-right js-toolbar-action non-mobile" aria-label="Sign in" href="register.html">
<i class="fa fa-sign-in"></i>
<span>登录</span>
</a>
<a class="btn pull-left js-toolbar-action btn-summary" href="#"><i class="fa fa-align-justify"></i></a>
<div class="dropdown pull-left font-settings js-toolbar-action">
<a class="btn toggle-dropdown" aria-label="Font Settings" href="#">
<i class="fa fa-font"></i>
</a>
<div class="dropdown-menu dropdown-right">
<div class="dropdown-caret"><span class="caret-outer"></span><span class="caret-inner"></span></div>
<div class="buttons">
<button class="button size-2 font-reduce">A</button>
<button class="button size-2 font-enlarge">A</button>
</div>
<div class="buttons">
<button class="button size-2 family-serif">Serif</button>
<button class="button size-2 family-sans">Sans</button>
</div>
<div class="buttons">
<button class="button size-3 theme-white">White</button>
<button class="button size-3 theme-sepia">Sepia</button>
<button class="button size-3 theme-night">Night</button>
</div>
</div>
</div>
<a class="btn pull-left js-toolbar-action non-mobile" aria-label="Home" href="en.html">
<i class="fa fa-home"></i> <span>首页</span>
</a>
<a class="btn pull-left js-toolbar-action non-mobile" aria-label="Guide" href="javascript:window.open('https://www.javadoc.org/');">
<i class="fa fa-book"></i> <span>API Docs</span>
</a>
<a class="btn pull-left js-toolbar-action non-mobile" aria-label="Tools" href="index37.html">
<i class="fa fa-gears"></i> <span>工具</span>
</a>
<div class="dropdown pull-left js-toolbar-action mobile">
<a class="btn toggle-dropdown" aria-label="Language" href="#">
<i class="fa fa-chevron-down"></i>
</a>
<div class="dropdown-menu dropdown-right">
<div class="dropdown-caret"><span class="caret-outer"></span><span class="caret-inner"></span></div>
<div class="buttons">
<a class="button size-1" aria-label="Home" href="en.html">
<i class="fa fa-home"></i> <span>首页</span>
</a>
</div>
<div class="buttons">
<a class="button size-1" aria-label="Guide" href="javascript:window.open('https://www.javadoc.org/');">
<i class="fa fa-book"></i> <span>API Docs</span>
</a>
</div>
<div class="buttons">
<a class="button size-1" aria-label="Tools" href="index37.html">
<i class="fa fa-gears"></i> <span>工具</span>
</a>
</div>
</div>
</div>
<div id="autocomplete" class="pull-right"></div>
<span id="toolbar-title"></span>
</header>
<div class="page-wrapper" tabindex="-1" role="main">
<div class="page-inner">
<section class="normal markdown-section">
<div id="content">
<h1>Spring Data Redis</h1>
<div><ins class="adsbygoogle" style="display:block; text-align:center;" data-ad-layout="in-article" data-ad-format="fluid" data-ad-client="ca-pub-6108808167664152" data-ad-slot="6964403648"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></div>
<div><div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>© 2011-2019 The original authors.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. </td>
</tr>
</tbody>
</table>
</div>
<div id="toc" class="toc">
<div id="toctitle" class="title">
Table of Contents
</div>
<ul class="sectlevel0">
<li><a href="#_preface">Preface</a>
<ul class="sectlevel1">
<li><a href="#new-features">1. New Features</a>
<ul class="sectlevel2">
<li><a href="#new-in-1.8.0">1.1. New in Spring Data Redis 1.8</a></li>
<li><a href="#new-in-1.7.0">1.2. New in Spring Data Redis 1.7</a></li>
<li><a href="#new-in-1-6-0">1.3. New in Spring Data Redis 1.6</a></li>
<li><a href="#new-in-1-5-0">1.4. New in Spring Data Redis 1.5</a></li>
</ul> </li>
</ul> </li>
<li><a href="#introduction">Introduction</a>
<ul class="sectlevel1">
<li><a href="#why-spring-redis">2. Why Spring Data Redis?</a></li>
<li><a href="#requirements">3. Requirements</a></li>
<li><a href="#get-started">4. Getting Started</a>
<ul class="sectlevel2">
<li><a href="#get-started:first-steps">4.1. First Steps</a>
<ul class="sectlevel3">
<li><a href="#get-started:first-steps:spring">4.1.1. Learning Spring</a></li>
<li><a href="#get-started:first-steps:nosql">4.1.2. Learning NoSQL and Key Value Stores</a></li>
<li><a href="#get-started:first-steps:samples">4.1.3. Trying out the Samples</a></li>
</ul> </li>
<li><a href="#get-started:help">4.2. Need Help?</a>
<ul class="sectlevel3">
<li><a href="#get-started:help:community">4.2.1. Community Support</a></li>
<li><a href="#get-started:help:professional">4.2.2. Professional Support</a></li>
</ul> </li>
<li><a href="#get-started:up-to-date">4.3. Following Development</a></li>
</ul> </li>
</ul> </li>
<li><a href="#reference">Reference Documentation</a>
<ul class="sectlevel1">
<li><a href="#redis">5. Redis support</a>
<ul class="sectlevel2">
<li><a href="#redis:requirements">5.1. Redis Requirements</a></li>
<li><a href="#redis:architecture">5.2. Redis Support High-level View</a></li>
<li><a href="#redis:connectors">5.3. Connecting to Redis</a>
<ul class="sectlevel3">
<li><a href="#redis:connectors:connection">5.3.1. RedisConnection and RedisConnectionFactory</a></li>
<li><a href="#redis:connectors:jedis">5.3.2. Configuring the Jedis Connector</a></li>
<li><a href="#redis:connectors:jredis">5.3.3. Configuring JRedis connector (Deprecated since 1.7)</a></li>
<li><a href="#redis:connectors:srp">5.3.4. Configuring SRP connector (Deprecated since 1.7)</a></li>
<li><a href="#redis:connectors:lettuce">5.3.5. Configuring the Lettuce connector</a></li>
</ul> </li>
<li><a href="#redis:sentinel">5.4. Redis Sentinel Support</a></li>
<li><a href="#redis:template">5.5. Working with Objects through RedisTemplate</a></li>
<li><a href="#redis:string">5.6. String-focused Convenience Classes</a></li>
<li><a href="#redis:serializer">5.7. Serializers</a></li>
<li><a href="#redis.hashmappers.root">5.8. Hash mapping</a>
<ul class="sectlevel3">
<li><a href="#_hash_mappers">5.8.1. Hash Mappers</a></li>
<li><a href="#redis.hashmappers.jackson2">5.8.2. Jackson2HashMapper</a></li>
</ul> </li>
<li><a href="#pubsub">5.9. Redis Messaging (Pub/Sub)</a>
<ul class="sectlevel3">
<li><a href="#redis:pubsub:publish">5.9.1. Publishing (Sending Messages)</a></li>
<li><a href="#redis:pubsub:subscribe">5.9.2. Subscribing (Receiving Messages)</a></li>
</ul> </li>
<li><a href="#tx">5.10. Redis Transactions</a>
<ul class="sectlevel3">
<li><a href="#tx.spring">5.10.1. @Transactional Support</a></li>
</ul> </li>
<li><a href="#pipeline">5.11. Pipelining</a></li>
<li><a href="#scripting">5.12. Redis Scripting</a></li>
<li><a href="#redis:support">5.13. Support Classes</a>
<ul class="sectlevel3">
<li><a href="#redis:support:cache-abstraction">5.13.1. Support for the Spring Cache Abstraction</a></li>
</ul> </li>
</ul> </li>
<li><a href="#cluster">6. Redis Cluster</a>
<ul class="sectlevel2">
<li><a href="#_enabling_redis_cluster">6.1. Enabling Redis Cluster</a></li>
<li><a href="#_working_with_redis_cluster_connection">6.2. Working With Redis Cluster Connection</a></li>
<li><a href="#_working_with_code_redistemplate_code_and_code_clusteroperations_code">6.3. Working with <code class="notranslate">RedisTemplate</code> and <code class="notranslate">ClusterOperations</code></a></li>
</ul> </li>
<li><a href="#redis.repositories">7. Redis Repositories</a>
<ul class="sectlevel2">
<li><a href="#redis.repositories.usage">7.1. Usage</a></li>
<li><a href="#redis.repositories.mapping">7.2. Object-to-Hash Mapping</a>
<ul class="sectlevel3">
<li><a href="#_customizing_type_mapping">7.2.1. Customizing Type Mapping</a></li>
</ul> </li>
<li><a href="#redis.repositories.keyspaces">7.3. Keyspaces</a></li>
<li><a href="#redis.repositories.indexes">7.4. Secondary Indexes</a>
<ul class="sectlevel3">
<li><a href="#redis.repositories.indexes.simple">7.4.1. Simple Property Index</a></li>
<li><a href="#redis.repositories.indexes.geospatial">7.4.2. Geospatial Index</a></li>
</ul> </li>
<li><a href="#redis.repositories.expirations">7.5. Time To Live</a></li>
<li><a href="#redis.repositories.references">7.6. Persisting References</a></li>
<li><a href="#redis.repositories.partial-updates">7.7. Persisting Partial Updates</a></li>
<li><a href="#redis.repositories.queries">7.8. Queries and Query Methods</a></li>
<li><a href="#redis.repositories.cluster">7.9. Redis Repositories Running on a Cluster</a></li>
<li><a href="#redis.repositories.cdi-integration">7.10. CDI Integration</a></li>
<li><a href="#_redis_repositories_anatomy">7.11. Redis Repositories Anatomy</a>
<ul class="sectlevel3">
<li><a href="#_insert_new">7.11.1. Insert new</a></li>
<li><a href="#_replace_existing">7.11.2. Replace existing</a></li>
<li><a href="#_save_geo_data">7.11.3. Save Geo Data</a></li>
<li><a href="#_find_using_simple_index">7.11.4. Find using simple index</a></li>
<li><a href="#_find_using_geo_index">7.11.5. Find using Geo Index</a></li>
</ul> </li>
</ul> </li>
</ul> </li>
<li><a href="#appendixes">Appendixes</a>
<ul class="sectlevel1">
<li><a href="#appendix:schema">Appendix A: Schema</a></li>
<li><a href="#appendix:command-reference">Appendix B: Command Reference</a>
<ul class="sectlevel2">
<li><a href="#_supported_commands">Supported Commands</a></li>
</ul> </li>
</ul> </li>
</ul>
</div>
</div>
</div>
<h2 id="_preface" class="sect0"><a class="anchor" href="#_preface"></a>Preface</h2>
<div class="openblock partintro">
<div class="content">
The Spring Data Redis project applies core Spring concepts to the development of solutions by using a key-value style data store. We provide a “template” as a high-level abstraction for sending and receiving messages. You may notice similarities to the JDBC support in the Spring Framework.
</div>
</div>
<div class="sect1">
<h2 id="new-features"><a class="anchor" href="#new-features"></a>1. New Features</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section briefly covers items that are new and noteworthy in the latest releases.</p>
</div>
<div class="sect2">
<h3 id="new-in-1.8.0"><a class="anchor" href="#new-in-1.8.0"></a>1.1. New in Spring Data Redis 1.8</h3>
<div class="ulist">
<ul>
<li> <p>Upgrade to Jedis 2.9.</p> </li>
<li> <p>Upgrade to <code class="notranslate">Lettuce</code> 4.2 (Note: Lettuce 4.2 requires Java 8).</p> </li>
<li> <p>Support for Redis <a href="javascript:window.open('http://redis.io/commands#geo');" target="_blank" rel="noopener noreferrer">GEO <i class="fa fa-external-link"></i></a> commands.</p> </li>
<li> <p>Support for Geospatial Indexes using Spring Data Repository abstractions (see <a href="#redis.repositories.indexes.geospatial">Geospatial Index</a>).</p> </li>
<li> <p><code class="notranslate">MappingRedisConverter</code>-based <code class="notranslate">HashMapper</code> implementation (see <a href="#redis.hashmappers.root">Hash mapping</a>).</p> </li>
<li> <p>Support for <code class="notranslate">PartialUpdate</code> in repositories (see <a href="#redis.repositories.partial-updates">Persisting Partial Updates</a>).</p> </li>
<li> <p>SSL support for connections to Redis cluster.</p> </li>
<li> <p>Support for <code class="notranslate">client name</code> through <code class="notranslate">ConnectionFactory</code> when using Jedis.</p> </li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="new-in-1.7.0"><a class="anchor" href="#new-in-1.7.0"></a>1.2. New in Spring Data Redis 1.7</h3>
<div class="ulist">
<ul>
<li> <p>Support for <a href="javascript:window.open('http://redis.io/topics/cluster-tutorial');" target="_blank" rel="noopener noreferrer">RedisCluster <i class="fa fa-external-link"></i></a>.</p> </li>
<li> <p>Support for Spring Data Repository abstractions (see <a href="#redis.repositories">Redis Repositories</a>).</p> </li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="new-in-1-6-0"><a class="anchor" href="#new-in-1-6-0"></a>1.3. New in Spring Data Redis 1.6</h3>
<div class="ulist">
<ul>
<li> <p>The <code class="notranslate">Lettuce</code> Redis driver switched from <a href="javascript:window.open('https://github.com/wg/lettuce');" target="_blank" rel="noopener noreferrer">wg/lettuce <i class="fa fa-external-link"></i></a> to <a href="javascript:window.open('https://github.com/mp911de/lettuce');" target="_blank" rel="noopener noreferrer">mp911de/lettuce <i class="fa fa-external-link"></i></a>.</p> </li>
<li> <p>Support for <code class="notranslate">ZRANGEBYLEX</code>.</p> </li>
<li> <p>Enhanced range operations for <code class="notranslate">ZSET</code>, including <code class="notranslate">+inf</code> / <code class="notranslate">-inf</code>.</p> </li>
<li> <p>Performance improvements in <code class="notranslate">RedisCache</code>, now releasing connections earlier.</p> </li>
<li> <p>Generic Jackson2 <code class="notranslate">RedisSerializer</code> making use of Jackson’s polymorphic deserialization.</p> </li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="new-in-1-5-0"><a class="anchor" href="#new-in-1-5-0"></a>1.4. New in Spring Data Redis 1.5</h3>
<div class="ulist">
<ul>
<li> <p>Add support for Redis HyperLogLog commands: <code class="notranslate">PFADD</code>, <code class="notranslate">PFCOUNT</code>, and <code class="notranslate">PFMERGE</code>.</p> </li>
<li> <p>Configurable <code class="notranslate">JavaType</code> lookup for Jackson-based <code class="notranslate">RedisSerializers</code>.</p> </li>
<li> <p><code class="notranslate">PropertySource</code>-based configuration for connecting to Redis Sentinel (see: <a href="#redis:sentinel">Redis Sentinel Support</a>).</p> </li>
</ul>
</div>
</div>
</div>
</div>
<h2 id="introduction" class="sect0"><a class="anchor" href="#introduction"></a>Introduction</h2>
<div class="openblock partintro">
<div class="content">
<div class="paragraph">
<p>This document is the reference guide for Spring Data Redis (SDR) Support. It explains Key-Value module concepts and semantics and the syntax for various stores namespaces.</p>
</div>
<div class="paragraph">
<p>For an introduction to key-value stores, Spring, or Spring Data examples, see <a href="#get-started">Getting Started</a>. This documentation refers only to Spring Data Redis Support and assumes the user is familiar with key-value storage and Spring concepts.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="why-spring-redis"><a class="anchor" href="#why-spring-redis"></a>2. Why Spring Data Redis?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Spring Framework is the leading full-stack Java/JEE application framework. It provides a lightweight container and a non-invasive programming model enabled by the use of dependency injection, AOP, and portable service abstractions.</p>
</div>
<div class="paragraph">
<p><a href="javascript:window.open('https://en.wikipedia.org/wiki/NoSQL');" target="_blank" rel="noopener noreferrer">NoSQL <i class="fa fa-external-link"></i></a> storage systems provide an alternative to classical RDBMS for horizontal scalability and speed. In terms of implementation, key-value stores represent one of the largest (and oldest) members in the NoSQL space.</p>
</div>
<div class="paragraph">
<p>The Spring Data Redis (SDR) framework makes it easy to write Spring applications that use the Redis key-value store by eliminating the redundant tasks and boilerplate code required for interacting with the store through Spring’s excellent infrastructure support.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="requirements"><a class="anchor" href="#requirements"></a>3. Requirements</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Spring Data Redis 2.x binaries require JDK level 8.0 and above and <a href="javascript:window.open('https://projects.spring.io/spring-framework/');" target="_blank" rel="noopener noreferrer">Spring Framework <i class="fa fa-external-link"></i></a> 4.3.22.RELEASE and above.</p>
</div>
<div class="paragraph">
<p>In terms of key-value stores, <a href="javascript:window.open('http://redis.io/');" target="_blank" rel="noopener noreferrer">Redis <i class="fa fa-external-link"></i></a> 2.6.x or higher is required. Spring Data Redis is currently tested against the latest 4.0 release.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="get-started"><a class="anchor" href="#get-started"></a>4. Getting Started</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section provides an easy-to-follow guide for getting started with the Spring Data Redis module.</p>
</div>
<div class="sect2">
<h3 id="get-started:first-steps"><a class="anchor" href="#get-started:first-steps"></a>4.1. First Steps</h3>
<div class="paragraph">
<p>As explained in <a href="#why-spring-redis">Why Spring Data Redis?</a>, Spring Data Redis (SDR) provides integration between the Spring framework and the Redis key-value store. Consequently, you should become acquainted with both of these frameworks. Throughout the SDR documentation, each section provides links to relevant resources. However, you should become familiar with these topics before reading this guide.</p>
</div>
<div class="sect3">
<h4 id="get-started:first-steps:spring"><a class="anchor" href="#get-started:first-steps:spring"></a>4.1.1. Learning Spring</h4>
<div class="paragraph">
<p>Spring Data uses Spring framework’s <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/spring-framework-reference/html/spring-core.html');" target="_blank" rel="noopener noreferrer">core <i class="fa fa-external-link"></i></a> functionality, such as the <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html');" target="_blank" rel="noopener noreferrer">IoC <i class="fa fa-external-link"></i></a> container, <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html');" target="_blank" rel="noopener noreferrer">resource <i class="fa fa-external-link"></i></a> abstract, and the <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html');" target="_blank" rel="noopener noreferrer">AOP <i class="fa fa-external-link"></i></a> infrastructure. While it is not important to know the Spring APIs, understanding the concepts behind them is important. At a minimum, the idea behind IoC should be familiar. That being said, the more knowledge you have about the Spring, the faster you can pick up Spring Data Redis. In addition to the Spring Framework’s comprehensive documentation, there are a lot of articles, blog entries, and books on the matter. The Spring Guides <a href="javascript:window.open('https://spring.io/guides');" target="_blank" rel="noopener noreferrer">home page <i class="fa fa-external-link"></i></a> offer a good place to start. In general, this should be the starting point for developers wanting to try Spring Data Redis.</p>
</div>
</div>
<div class="sect3">
<h4 id="get-started:first-steps:nosql"><a class="anchor" href="#get-started:first-steps:nosql"></a>4.1.2. Learning NoSQL and Key Value Stores</h4>
<div class="paragraph">
<p>NoSQL stores have taken the storage world by storm. It is a vast domain with a plethora of solutions, terms, and patterns (to make things worse, even the term itself has multiple <a href="javascript:window.open('https://www.google.com/search?q=nosoql+acronym');" target="_blank" rel="noopener noreferrer">meanings <i class="fa fa-external-link"></i></a>). While some of the principles are common, it is crucial that you be familiar to some degree with the stores supported by SDR. The best way to get acquainted with these solutions is to read their documentation and follow their examples. It usually does not take more then five to ten minutes to go through them and, if you come from an RDMBS-only background, many times these exercises can be eye-openers.</p>
</div>
</div>
<div class="sect3">
<h4 id="get-started:first-steps:samples"><a class="anchor" href="#get-started:first-steps:samples"></a>4.1.3. Trying out the Samples</h4>
<div class="paragraph">
<p>One can find various samples for key-value stores in the dedicated Spring Data example repo, at <a href="javascript:window.open('https://github.com/spring-projects/spring-data-keyvalue-examples');" target="_blank" rel="noopener noreferrer">http://github.com/spring-projects/spring-data-keyvalue-examples <i class="fa fa-external-link"></i></a>. For Spring Data Redis, you should pay particular attention to the <code class="notranslate">retwisj</code> sample, a Twitter-clone built on top of Redis that can be run locally or be deployed into the cloud. See its <a href="javascript:window.open('http://static.springsource.org/spring-data/data-keyvalue/examples/retwisj/current/');" target="_blank" rel="noopener noreferrer">documentation <i class="fa fa-external-link"></i></a>, the following blog <a href="javascript:window.open('http://blog.springsource.com/2011/04/27/getting-started-redis-spring-cloud-foundry/');" target="_blank" rel="noopener noreferrer">entry <i class="fa fa-external-link"></i></a> for more information.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="get-started:help"><a class="anchor" href="#get-started:help"></a>4.2. Need Help?</h3>
<div class="paragraph">
<p>If you encounter issues or you are just looking for advice, use one of the links below:</p>
</div>
<div class="sect3">
<h4 id="get-started:help:community"><a class="anchor" href="#get-started:help:community"></a>4.2.1. Community Support</h4>
<div class="paragraph">
<p>The Spring Data tag on <a href="javascript:window.open('https://stackoverflow.com/questions/tagged/spring-data');" target="_blank" rel="noopener noreferrer">Stack Overflow <i class="fa fa-external-link"></i></a> is a message board for all Spring Data (not just Redis) users to share information and help each other. Note that registration is needed <strong>only</strong> for posting.</p>
</div>
</div>
<div class="sect3">
<h4 id="get-started:help:professional"><a class="anchor" href="#get-started:help:professional"></a>4.2.2. Professional Support</h4>
<div class="paragraph">
<p>Professional, from-the-source support, with guaranteed response time, is available from <a href="javascript:window.open('http://www.gopivotal.com/');" target="_blank" rel="noopener noreferrer">Pivotal Software, Inc. <i class="fa fa-external-link"></i></a>, the company behind Spring Data and Spring.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="get-started:up-to-date"><a class="anchor" href="#get-started:up-to-date"></a>4.3. Following Development</h3>
<div class="paragraph">
<p>For information on the Spring Data source code repository, nightly builds, and snapshot artifacts, see the Spring Data home <a href="javascript:window.open('https://spring.io/spring-data');" target="_blank" rel="noopener noreferrer">page <i class="fa fa-external-link"></i></a>.</p>
</div>
<div class="paragraph">
<p>You can help make Spring Data best serve the needs of the Spring community by interacting with developers on Stack Overflow at either <a href="javascript:window.open('https://stackoverflow.com/questions/tagged/spring-data');" target="_blank" rel="noopener noreferrer">spring-data <i class="fa fa-external-link"></i></a> or <a href="javascript:window.open('https://stackoverflow.com/questions/tagged/spring-data-redis');" target="_blank" rel="noopener noreferrer">spring-data-redis <i class="fa fa-external-link"></i></a>.</p>
</div>
<div class="paragraph">
<p>If you encounter a bug or want to suggest an improvement (including to this documentation), please create a ticket on the Spring Data issue <a href="javascript:window.open('https://jira.spring.io/browse/DATAREDIS');" target="_blank" rel="noopener noreferrer">tracker <i class="fa fa-external-link"></i></a>.</p>
</div>
<div class="paragraph">
<p>To stay up to date with the latest news and announcements in the Spring eco system, subscribe to the Spring Community <a href="javascript:window.open('https://spring.io/');" target="_blank" rel="noopener noreferrer">Portal <i class="fa fa-external-link"></i></a>.</p>
</div>
<div class="paragraph">
<p>Lastly, you can follow the Spring <a href="javascript:window.open('https://spring.io/blog/');" target="_blank" rel="noopener noreferrer">blog <i class="fa fa-external-link"></i></a> or the project team (<a href="javascript:window.open('https://twitter.com/thomasdarimont');" target="_blank" rel="noopener noreferrer">Thomas <i class="fa fa-external-link"></i></a> and <a href="javascript:window.open('https://twitter.com/stroblchristoph');" target="_blank" rel="noopener noreferrer">Christoph <i class="fa fa-external-link"></i></a>) on Twitter.</p>
</div>
</div>
</div>
</div>
<h2 id="reference" class="sect0"><a class="anchor" href="#reference"></a>Reference Documentation</h2>
<div class="openblock partintro">
<div class="content">
<h2 id="_document_structure" class="float">Document structure</h2>
<div class="paragraph">
<p>This part of the reference documentation explains the core functionality offered by Spring Data Redis.</p>
</div>
<div class="paragraph">
<p><a href="#redis">Redis support</a> introduces the Redis module feature set.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="redis"><a class="anchor" href="#redis"></a>5. Redis support</h2>
<div class="sectionbody">
<div class="paragraph">
<p>One of the key-value stores supported by Spring Data is <a href="javascript:window.open('http://redis.io/');" target="_blank" rel="noopener noreferrer">Redis <i class="fa fa-external-link"></i></a>. To quote the Redis project home page:</p>
</div>
<div class="quoteblock">
<blockquote>
Redis is an advanced key-value store. It is similar to memcached but the dataset is not volatile, and values can be strings, exactly like in memcached, but also lists, sets, and ordered sets. All this data types can be manipulated with atomic operations to push/pop elements, add/remove elements, perform server side union, intersection, difference between sets, and so forth. Redis supports different kind of sorting abilities.
</blockquote>
</div>
<div class="paragraph">
<p>Spring Data Redis provides easy configuration and access to Redis from Spring applications. It offers both low-level and high-level abstractions for interacting with the store, freeing the user from infrastructural concerns.</p>
</div>
<div class="sect2">
<h3 id="redis:requirements"><a class="anchor" href="#redis:requirements"></a>5.1. Redis Requirements</h3>
<div class="paragraph">
<p>Spring Data Redis requires Redis 2.6 or above and Java SE 6.0 or above . In terms of language bindings (or connectors), Spring Redis integrates with <a href="javascript:window.open('https://github.com/xetorthio/jedis');" target="_blank" rel="noopener noreferrer">Jedis <i class="fa fa-external-link"></i></a>, <a href="javascript:window.open('https://github.com/alphazero/jredis');" target="_blank" rel="noopener noreferrer">JRedis <i class="fa fa-external-link"></i></a> (Deprecated since 1.7), <a href="javascript:window.open('https://github.com/spullara/redis-protocol');" target="_blank" rel="noopener noreferrer">SRP <i class="fa fa-external-link"></i></a> (Deprecated since 1.7) and <a href="javascript:window.open('https://github.com/wg/lettuce');" target="_blank" rel="noopener noreferrer">Lettuce <i class="fa fa-external-link"></i></a>, four popular open-source Java libraries for Redis. If you are aware of any other connector that we should be integrating with please send us feedback.</p>
</div>
</div>
<div class="sect2">
<h3 id="redis:architecture"><a class="anchor" href="#redis:architecture"></a>5.2. Redis Support High-level View</h3>
<div class="paragraph">
<p>The Redis support provides several components. For most tasks, the high-level abstractions and support services are the best choice. Note that, at any point, you can move between layers. For example, you can get a low-level connection (or even the native library) to communicate directly with Redis.</p>
</div>
</div>
<div class="sect2">
<h3 id="redis:connectors"><a class="anchor" href="#redis:connectors"></a>5.3. Connecting to Redis</h3>
<div class="paragraph">
<p>One of the first tasks when using Redis and Spring is to connect to the store through the IoC container. To do that, a Java connector (or binding) is required. No matter the library you choose, you need to use only one set of Spring Data Redis APIs (which behaves consistently across all connectors): the <code class="notranslate">org.springframework.data.redis.connection</code> package and its <code class="notranslate">RedisConnection</code> and <code class="notranslate">RedisConnectionFactory</code> interfaces for working with and retrieving active connections to Redis.</p>
</div>
<div class="sect3">
<h4 id="redis:connectors:connection"><a class="anchor" href="#redis:connectors:connection"></a>5.3.1. RedisConnection and RedisConnectionFactory</h4>
<div class="paragraph">
<p><code class="notranslate">RedisConnection</code> provides the core building block for Redis communication, as it handles the communication with the Redis back end. It also automatically translates the underlying connecting library exceptions to Spring’s consistent DAO exception <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions');" target="_blank" rel="noopener noreferrer">hierarchy <i class="fa fa-external-link"></i></a> so that you can switch the connectors without any code changes, as the operation semantics remain the same.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> For the corner cases where the native library API is required, <code class="notranslate">RedisConnection</code> provides a dedicated method (<code class="notranslate">getNativeConnection</code>) that returns the raw, underlying object used for communication. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>Active <code class="notranslate">RedisConnection</code> objects are created through <code class="notranslate">RedisConnectionFactory</code>. In addition, the factory acts as <code class="notranslate">PersistenceExceptionTranslator</code> objects, meaning that, once declared, they let you do transparent exception translation. For example, you can do exception translation through the use of the <code class="notranslate">@Repository</code> annotation and AOP. For more information, see the dedicated <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html#orm-exception-translation');" target="_blank" rel="noopener noreferrer">section <i class="fa fa-external-link"></i></a> in the Spring Framework documentation.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Depending on the underlying configuration, the factory can return a new connection or an existing connection (when a pool or shared native connection is used). </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>The easiest way to work with a <code class="notranslate">RedisConnectionFactory</code> is to configure the appropriate connector through the IoC container and inject it into the using class.</p>
</div>
<div class="admonitionblock important">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-important" title="Important"></i> </td>
<td class="content"> Unfortunately, currently, not all connectors support all Redis features. When invoking a method on the Connection API that is unsupported by the underlying library, an <code class="notranslate">UnsupportedOperationException</code> is thrown. </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect3">
<h4 id="redis:connectors:jedis"><a class="anchor" href="#redis:connectors:jedis"></a>5.3.2. Configuring the Jedis Connector</h4>
<div class="paragraph">
<p><a href="javascript:window.open('https://github.com/xetorthio/jedis');" target="_blank" rel="noopener noreferrer">Jedis <i class="fa fa-external-link"></i></a> is one of the connectors supported by the Spring Data Redis module through the <code class="notranslate">org.springframework.data.redis.connection.jedis</code> package. In its simplest form, the Jedis configuration looks as follow:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;!-- Jedis ConnectionFactory --&gt;
  &lt;bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"/&gt;

&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>For production use, however, you might want to tweak settings such as the host or password, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="server" p:port="6379" /&gt;

&lt;/beans&gt;</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="redis:connectors:jredis"><a class="anchor" href="#redis:connectors:jredis"></a>5.3.3. Configuring JRedis connector (Deprecated since 1.7)</h4>
<div class="paragraph">
<p><a href="javascript:window.open('https://github.com/alphazero/jredis');" target="_blank" rel="noopener noreferrer">JRedis <i class="fa fa-external-link"></i></a> is another popular, open-source connector supported by Spring Data Redis through the <code class="notranslate">org.springframework.data.redis.connection.jredis</code> package.</p>
</div>
<div class="paragraph">
<p>A typical JRedis configuration can looks like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="jredisConnectionFactory" class="org.springframework.data.redis.connection.jredis.JredisConnectionFactory" p:host-name="server" p:port="6379"/&gt;

&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The configuration is quite similar to Jedis, with one notable exception. By default, the <code class="notranslate">JedisConnectionFactory</code> pools connections. In order to use a connection pool with JRedis, configure the <code class="notranslate">JredisConnectionFactory</code> with an instance of <code class="notranslate">JredisPool</code>. For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="jredisConnectionFactory" class="org.springframework.data.redis.connection.jredis.JredisConnectionFactory"&gt;
    &lt;constructor-arg&gt;
      &lt;bean class="org.springframework.data.redis.connection.jredis.DefaultJredisPool"&gt;
        &lt;constructor-arg value="localhost" /&gt;
        &lt;constructor-arg value="6379" /&gt;
      &lt;/bean&gt;
    &lt;/constructor-arg&gt;
  &lt;/bean&gt;

&lt;/beans&gt;</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="redis:connectors:srp"><a class="anchor" href="#redis:connectors:srp"></a>5.3.4. Configuring SRP connector (Deprecated since 1.7)</h4>
<div class="paragraph">
<p><a href="javascript:window.open('https://github.com/spullara/redis-protocol');" target="_blank" rel="noopener noreferrer">SRP <i class="fa fa-external-link"></i></a> (an acronym for Sam’s Redis Protocol) is the third open-source connector supported by Spring Data Redis through the <code class="notranslate">org.springframework.data.redis.connection.srp</code> package.</p>
</div>
<div class="paragraph">
<p>By now, its configuration is probably easy to guess:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="srpConnectionFactory" class="org.springframework.data.redis.connection.srp.SrpConnectionFactory" p:host-name="server" p:port="6379"/&gt;

&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Needless to say, the configuration is quite similar to that of the other connectors.</p>
</div>
</div>
<div class="sect3">
<h4 id="redis:connectors:lettuce"><a class="anchor" href="#redis:connectors:lettuce"></a>5.3.5. Configuring the Lettuce connector</h4>
<div class="paragraph">
<p><a href="javascript:window.open('https://github.com/mp911de/lettuce');" target="_blank" rel="noopener noreferrer">Lettuce <i class="fa fa-external-link"></i></a> is the fourth open-source connector supported by Spring Data Redis through the <code class="notranslate">org.springframework.data.redis.connection.lettuce</code> package.</p>
</div>
<div class="paragraph">
<p>Its configuration is probably easy to guess:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="lettuceConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" p:host-name="server" p:port="6379"/&gt;

&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>There are also a few Lettuce-specific connection parameters that can be tweaked. By default, all <code class="notranslate">LettuceConnection</code> s created by the <code class="notranslate">LettuceConnectionFactory</code> share the same thread-safe native connection for all non-blocking and non-transactional operations. Set <code class="notranslate">shareNativeConnection</code> to false to use a dedicated connection each time. <code class="notranslate">LettuceConnectionFactory</code> can also be configured with a <code class="notranslate">LettucePool</code> to use for pooling blocking and transactional connections, or all connections if <code class="notranslate">shareNativeConnection</code> is set to false.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis:sentinel"><a class="anchor" href="#redis:sentinel"></a>5.4. Redis Sentinel Support</h3>
<div class="paragraph">
<p>For dealing with high-availability Redis, Spring Data Redis has support for <a href="javascript:window.open('http://redis.io/topics/sentinel');" target="_blank" rel="noopener noreferrer">Redis Sentinel <i class="fa fa-external-link"></i></a>, using <code class="notranslate">RedisSentinelConfiguration</code>, as shown in the following example:</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Please note that currently only <a href="javascript:window.open('https://github.com/xetorthio/jedis');" target="_blank" rel="noopener noreferrer">Jedis <i class="fa fa-external-link"></i></a> and lettuce <a href="javascript:window.open('https://github.com/mp911de/lettuce');" target="_blank" rel="noopener noreferrer">Lettuce <i class="fa fa-external-link"></i></a> support Redis Sentinel. </td>
</tr>
</tbody>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">/**
 * jedis
 */
@Bean
public RedisConnectionFactory jedisConnectionFactory() {
  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration() .master("mymaster")
  .sentinel("127.0.0.1", 26379) .sentinel("127.0.0.1", 26380);
  return new JedisConnectionFactory(sentinelConfig);
}

/**
 * lettuce
 */
@Bean
public RedisConnectionFactory lettuceConnectionFactory() {
  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration().master("mymaster")
  .sentinel("127.0.0.1", 26379) .sentinel("127.0.0.1", 26380);
  return new LettuceConnectionFactory(sentinelConfig);
}</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td>
<td class="content">
<div class="paragraph">
<p><code class="notranslate">RedisSentinelConfiguration</code> can also be defined with a <code class="notranslate">PropertySource</code>, which lets you set the following properties:</p>
</div>
<div class="ulist">
<div class="title">
Configuration Properties
</div>
<ul>
<li> <p><code class="notranslate">spring.redis.sentinel.master</code>: name of the master node.</p> </li>
<li> <p><code class="notranslate">spring.redis.sentinel.nodes</code>: Comma delimited list of host:port pairs.</p> </li>
</ul>
</div> </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>Sometimes, direct interaction with one of the Sentinels is required. Using <code class="notranslate">RedisConnectionFactory.getSentinelConnection()</code> or <code class="notranslate">RedisConnection.getSentinelCommands()</code> gives you access to the first active Sentinel configured.</p>
</div>
</div>
<div class="sect2">
<h3 id="redis:template"><a class="anchor" href="#redis:template"></a>5.5. Working with Objects through RedisTemplate</h3>
<div class="paragraph">
<p>Most users are likely to use <code class="notranslate">RedisTemplate</code> and its coresponding package, <code class="notranslate">org.springframework.data.redis.core</code>. The template is, in fact, the central class of the Redis module, due to its rich feature set. The template offers a high-level abstraction for Redis interactions. While <code class="notranslate">RedisConnection</code> offers low-level methods that accept and return binary values (<code class="notranslate">byte</code> arrays), the template takes care of serialization and connection management, freeing the user from dealing with such details.</p>
</div>
<div class="paragraph">
<p>Moreover, the template provides operations views (following the grouping from the Redis command <a href="javascript:window.open('http://redis.io/commands');" target="_blank" rel="noopener noreferrer">reference <i class="fa fa-external-link"></i></a>) that offer rich, generified interfaces for working against a certain type or certain key (through the <code class="notranslate">KeyBound</code> interfaces) as described in the following table:</p>
</div>
<table class="tableblock frame-all grid-all" style="width: 80%;">
<caption class="title">
Table 1. Operational views
</caption>
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 66.6667%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Interface</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-center valign-top" colspan="2"><p class="tableblock"><em>Key Type Operations</em></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">GeoOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis geospatial operations, such as <code class="notranslate">GEOADD</code>, <code class="notranslate">GEORADIUS</code>,…</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">HashOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis hash operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">HyperLogLogOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis HyperLogLog operations, such as <code class="notranslate">PFADD</code>, <code class="notranslate">PFCOUNT</code>,…</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">ListOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis list operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">SetOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis set operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">ValueOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis string (or value) operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">ZSetOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis zset (or sorted set) operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HashOperations</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis hash operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HyperLogLogOperations</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis HyperLogLog operations like (pfadd, pfcount,…)</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">GeoOperations</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis geospatial operations like <code class="notranslate">GEOADD</code>, <code class="notranslate">GEORADIUS</code>,…)</p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top" colspan="2"><p class="tableblock"><em>Key Bound Operations</em></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">BoundGeoOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis key bound geospatial operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">BoundHashOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis hash key bound operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">BoundKeyOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis key bound operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">BoundListOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis list key bound operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">BoundSetOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis set key bound operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">BoundValueOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis string (or value) key bound operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">BoundZSetOperations</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis zset (or sorted set) key bound operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BoundHashOperations</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis hash key bound operations</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BoundGeoOperations</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Redis key bound geospatial operations.</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>Once configured, the template is thread-safe and can be reused across multiple instances.</p>
</div>
<div class="paragraph">
<p><code class="notranslate">RedisTemplate</code> uses a Java-based serializer for most of its operations. This means that any object written or read by the template is serialized and deserialized through Java. You can change the serialization mechanism on the template, and the Redis module offers several implementations, which are available in the <code class="notranslate">org.springframework.data.redis.serializer</code> package. See <a href="#redis:serializer">Serializers</a> for more information. You can also set any of the serializers to null and use RedisTemplate with raw byte arrays by setting the <code class="notranslate">enableDefaultSerializer</code> property to <code class="notranslate">false</code>. Note that the template requires all keys to be non-null. However, values can be null as long as the underlying serializer accepts them. Read the Javadoc of each serializer for more information.</p>
</div>
<div class="paragraph">
<p>For cases where you need a certain template view, declare the view as a dependency and inject the template. The container automatically performs the conversion, eliminating the <code class="notranslate">opsFor[X]</code> calls, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/&gt;
  &lt;!-- redis template definition --&gt;
  &lt;bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/&gt;
  ...

&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class Example {

  // inject the actual template
  @Autowired
  private RedisTemplate&lt;String, String&gt; template;

  // inject the template as ListOperations
  @Resource(name="redisTemplate")
  private ListOperations&lt;String, String&gt; listOps;

  public void addLink(String userId, URL url) {
    listOps.leftPush(userId, url.toExternalForm());
  }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis:string"><a class="anchor" href="#redis:string"></a>5.6. String-focused Convenience Classes</h3>
<div class="paragraph">
<p>Since it is quite common for the keys and values stored in Redis to be <code class="notranslate">java.lang.String</code>, the Redis modules provides two extensions to <code class="notranslate">RedisConnection</code> and <code class="notranslate">RedisTemplate</code>, respectively the <code class="notranslate">StringRedisConnection</code> (and its <code class="notranslate">DefaultStringRedisConnection</code> implementation) and <code class="notranslate">StringRedisTemplate</code> as a convenient one-stop solution for intensive String operations. In addition to being bound to <code class="notranslate">String</code> keys, the template and the connection use the <code class="notranslate">StringRedisSerializer</code> underneath, which means the stored keys and values are human-readable (assuming the same encoding is used both in Redis and your code). The following listings show an example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/&gt;

  &lt;bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/&gt;
  ...
&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class Example {

  @Autowired
  private StringRedisTemplate redisTemplate;

  public void addLink(String userId, URL url) {
    redisTemplate.opsForList().leftPush(userId, url.toExternalForm());
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>As with the other Spring templates, <code class="notranslate">RedisTemplate</code> and <code class="notranslate">StringRedisTemplate</code> let you talk directly to Redis through the <code class="notranslate">RedisCallback</code> interface. This feature gives complete control to you, as it talks directly to the <code class="notranslate">RedisConnection</code>. Note that the callback receives an instance of <code class="notranslate">StringRedisConnection</code> when a <code class="notranslate">StringRedisTemplate</code> is used. The following example shows how to use the <code class="notranslate">RedisCallback</code> interface:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public void useCallback() {

  redisTemplate.execute(new RedisCallback&lt;Object&gt;() {
    public Object doInRedis(RedisConnection connection) throws DataAccessException {
      Long size = connection.dbSize();
      // Can cast to StringRedisConnection if using a StringRedisTemplate
      ((StringRedisConnection)connection).set("key", "value");
    }
   });
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis:serializer"><a class="anchor" href="#redis:serializer"></a>5.7. Serializers</h3>
<div class="paragraph">
<p>From the framework perspective, the data stored in Redis is only bytes. While Redis itself supports various types, for the most part, these refer to the way the data is stored rather than what it represents. It is up to the user to decide whether the information gets translated into strings or any other objects.</p>
</div>
<div class="paragraph">
<p>In Spring Data, the conversion between the user (custom) types and raw data (and vice-versa) is handled Redis in the <code class="notranslate">org.springframework.data.redis.serializer</code> package.</p>
</div>
<div class="paragraph">
<p>This package contains two types of serializers that, as the name implies, take care of the serialization process:</p>
</div>
<div class="ulist">
<ul>
<li> <p>Two-way serializers based on <code class="notranslate">RedisSerializer</code>.</p> </li>
<li> <p>Element readers and writers that use <code class="notranslate">RedisElementReader</code> and <code class="notranslate">RedisElementWriter</code>.</p> </li>
</ul>
</div>
<div class="paragraph">
<p>The main difference between these variants is that <code class="notranslate">RedisSerializer</code> primarily serializes to <code class="notranslate">byte[]</code> while readers and writers use <code class="notranslate">ByteBuffer</code>.</p>
</div>
<div class="paragraph">
<p>Multiple implementations are available (including two that have been already mentioned in this documentation):</p>
</div>
<div class="ulist">
<ul>
<li> <p><code class="notranslate">JdkSerializationRedisSerializer</code>, which is used by default for <code class="notranslate">RedisCache</code> and <code class="notranslate">RedisTemplate</code>.</p> </li>
<li> <p>the <code class="notranslate">StringRedisSerializer</code>.</p> </li>
</ul>
</div>
<div class="paragraph">
<p>However one can use <code class="notranslate">OxmSerializer</code> for Object/XML mapping through Spring <a href="javascript:window.open('https://docs.spring.io/spring/docs/4.3.22.RELEASE/spring-framework-reference/data-access.html#oxm');" target="_blank" rel="noopener noreferrer">OXM <i class="fa fa-external-link"></i></a> support or <code class="notranslate">Jackson2JsonRedisSerializer</code> or <code class="notranslate">GenericJackson2JsonRedisSerializer</code> for storing data in <a href="javascript:window.open('https://en.wikipedia.org/wiki/JSON');" target="_blank" rel="noopener noreferrer">JSON <i class="fa fa-external-link"></i></a> format.</p>
</div>
<div class="paragraph">
<p>Do note that the storage format is not limited only to values. It can be used for keys, values, or hashes without any restrictions.</p>
</div>
<div class="admonitionblock warning">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-warning" title="Warning"></i> </td>
<td class="content">
<div class="paragraph">
<p>By default, <code class="notranslate">RedisCache</code> and <code class="notranslate">RedisTemplate</code> are configured to use Java native serialization. Java native serialization is known for allowing remote code execution caused by payloads that exploit vulnerable libraries and classes injecting unverified bytecode. Manipulated input could lead to unwanted code execution in the application during the deserialization step. As a consequence, do not use serialization in untrusted environments. In general, we strongly recommend any other message format (such as JSON) instead.</p>
</div>
<div class="paragraph">
<p>If you are concerned about security vulnerabilities due to Java serialization, consider the general-purpose serialization filter mechanism at the core JVM level, originally developed for JDK 9 but backported to JDK 8, 7, and 6:</p>
</div>
<div class="ulist">
<ul>
<li> <p><a href="javascript:window.open('https://blogs.oracle.com/java-platform-group/entry/incoming_filter_serialization_data_a');" target="_blank" rel="noopener noreferrer">Filter Incoming Serialization Data <i class="fa fa-external-link"></i></a>.</p> </li>
<li> <p><a href="javascript:window.open('https://openjdk.java.net/jeps/290');" target="_blank" rel="noopener noreferrer">JEP 290 <i class="fa fa-external-link"></i></a>.</p> </li>
<li> <p><a href="javascript:window.open('https://www.owasp.org/index.php/Deserialization_of_untrusted_data');" target="_blank" rel="noopener noreferrer">OWASP: Deserialization of untrusted data <i class="fa fa-external-link"></i></a>.</p> </li>
</ul>
</div> </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="redis.hashmappers.root"><a class="anchor" href="#redis.hashmappers.root"></a>5.8. Hash mapping</h3>
<div class="paragraph">
<p>Data can be stored by using various data structures within Redis. <code class="notranslate">Jackson2JsonRedisSerializer</code> can convert objects in <a href="javascript:window.open('https://en.wikipedia.org/wiki/JSON');" target="_blank" rel="noopener noreferrer">JSON <i class="fa fa-external-link"></i></a> format. Ideally, JSON can be stored as a value by using plain keys. You can achieve a more sophisticated mapping of structured objects by using Redis hashes. Spring Data Redis offers various strategies for mapping data to hashes (depending on the use case):</p>
</div>
<div class="ulist">
<ul>
<li> <p>Direct mapping, by using <code class="notranslate">HashOperations</code> and a <a href="#redis:serializer">serializer</a></p> </li>
<li> <p>Using <a href="#redis.repositories">Redis Repositories</a></p> </li>
<li> <p>Using <code class="notranslate">HashMapper</code> and <code class="notranslate">HashOperations</code></p> </li>
</ul>
</div>
<div class="sect3">
<h4 id="_hash_mappers"><a class="anchor" href="#_hash_mappers"></a>5.8.1. Hash Mappers</h4>
<div class="paragraph">
<p>Hash mappers are converters of map objects to a <code class="notranslate">Map&lt;K, V&gt;</code> and back. <code class="notranslate">HashMapper</code> is intended for using with Redis Hashes.</p>
</div>
<div class="paragraph">
<p>Multiple implementations are available:</p>
</div>
<div class="ulist">
<ul>
<li> <p><code class="notranslate">BeanUtilsHashMapper</code> using Spring’s <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html');" target="_blank" rel="noopener noreferrer">BeanUtils <i class="fa fa-external-link"></i></a>.</p> </li>
<li> <p><code class="notranslate">ObjectHashMapper</code> using <a href="#redis.repositories.mapping">Object-to-Hash Mapping</a>.</p> </li>
<li> <p><a href="#redis.hashmappers.jackson2"><code class="notranslate">Jackson2HashMapper</code></a> using <a href="javascript:window.open('https://github.com/FasterXML/jackson');" target="_blank" rel="noopener noreferrer">FasterXML Jackson <i class="fa fa-external-link"></i></a>.</p> </li>
</ul>
</div>
<div class="paragraph">
<p>The following example shows one way to implement hash mapping:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class Person {
  String firstname;
  String lastname;

  // …
}

public class HashMapping {

  @Autowired
  HashOperations&lt;String, byte[], byte[]&gt; hashOperations;

  HashMapper&lt;Object, byte[], byte[]&gt; mapper = new ObjectHashMapper();

  public void writeHash(String key, Person person) {

    Map&lt;byte[], byte[]&gt; mappedHash = mapper.toHash(person);
    hashOperations.putAll(key, mappedHash);
  }

  public Person loadHash(String key) {

    Map&lt;byte[], byte[]&gt; loadedHash = hashOperations.entries("key");
    return (Person) mapper.fromHash(loadedHash);
  }
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="redis.hashmappers.jackson2"><a class="anchor" href="#redis.hashmappers.jackson2"></a>5.8.2. Jackson2HashMapper</h4>
<div class="paragraph">
<p><code class="notranslate">Jackson2HashMapper</code> provides Redis Hash mapping for domain objects by using <a href="javascript:window.open('https://github.com/FasterXML/jackson');" target="_blank" rel="noopener noreferrer">FasterXML Jackson <i class="fa fa-external-link"></i></a>. <code class="notranslate">Jackson2HashMapper</code> can map top-level properties as Hash field names and, optionally, flatten the structure. Simple types map to simple values. Complex types (nested objects, collections, maps, and so on) are represented as nested JSON.</p>
</div>
<div class="paragraph">
<p>Flattening creates individual hash entries for all nested properties and resolves complex types into simple types, as far as possible.</p>
</div>
<div class="paragraph">
<p>Consider the following class and the data structure it contains:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class Person {
  String firstname;
  String lastname;
  Address address;
}

public class Address {
  String city;
  String country;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following table shows how the data in the preceding class would appear in normal mapping:</p>
</div>
<table class="tableblock frame-all grid-all" style="width: 80%;">
<caption class="title">
Table 2. Normal Mapping
</caption>
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 66.6667%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Hash Field</th>
<th class="tableblock halign-left valign-top">Value</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">firstname</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Jon</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">lastname</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Snow</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">address</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">{ "city" : "Castle Black", "country" : "The North" }</code></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>The following table shows how the data in the preceding class would appear in flat mapping:</p>
</div>
<table class="tableblock frame-all grid-all" style="width: 80%;">
<caption class="title">
Table 3. Flat Mapping
</caption>
<colgroup>
<col style="width: 33.3333%;">
<col style="width: 66.6667%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Hash Field</th>
<th class="tableblock halign-left valign-top">Value</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">firstname</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Jon</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">lastname</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Snow</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">address.city</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Castle Black</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">address.country</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">The North</code></p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Flattening requires all property names to not interfere with the JSON path. Using dots or brackets in map keys or as property names is not supported when you use flattening. The resulting hash cannot be mapped back into an Object. </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="pubsub"><a class="anchor" href="#pubsub"></a>5.9. Redis Messaging (Pub/Sub)</h3>
<div class="paragraph">
<p>Spring Data provides dedicated messaging integration for Redis, similar in functionality and naming to the JMS integration in Spring Framework.</p>
</div>
<div class="paragraph">
<p>Redis messaging can be roughly divided into two areas of functionality:</p>
</div>
<div class="ulist">
<ul>
<li> <p>Publication or production of messages</p> </li>
<li> <p>Subscription or consumption of messages</p> </li>
</ul>
</div>
<div class="paragraph">
<p>This is an example of the pattern often called Publish/Subscribe (Pub/Sub for short). The <code class="notranslate">RedisTemplate</code> class is used for message production. For asynchronous reception similar to Java EE’s message-driven bean style, Spring Data provides a dedicated message listener container that is used to create Message-Driven POJOs (MDPs) and, for synchronous reception, the <code class="notranslate">RedisConnection</code> contract.</p>
</div>
<div class="paragraph">
<p>The <code class="notranslate">org.springframework.data.redis.connection</code> and <code class="notranslate">org.springframework.data.redis.listener</code> packages provide the core functionality for Redis messaging.</p>
</div>
<div class="sect3">
<h4 id="redis:pubsub:publish"><a class="anchor" href="#redis:pubsub:publish"></a>5.9.1. Publishing (Sending Messages)</h4>
<div class="paragraph">
<p>To publish a message, you can use, as with the other operations, either the low-level <code class="notranslate">RedisConnection</code> or the high-level <code class="notranslate">RedisTemplate</code>. Both entities offer the <code class="notranslate">publish</code> method, which accepts the message and the destination channel as arguments. While <code class="notranslate">RedisConnection</code> requires raw data (array of bytes), the <code class="notranslate">RedisTemplate</code> lets arbitrary objects be passed in as messages, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">// send message through connection RedisConnection con = ...
byte[] msg = ...
byte[] channel = ...
con.publish(msg, channel); // send message through RedisTemplate
RedisTemplate template = ...
template.convertAndSend("hello!", "world");</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="redis:pubsub:subscribe"><a class="anchor" href="#redis:pubsub:subscribe"></a>5.9.2. Subscribing (Receiving Messages)</h4>
<div class="paragraph">
<p>On the receiving side, one can subscribe to one or multiple channels either by naming them directly or by using pattern matching. The latter approach is quite useful, as it not only lets multiple subscriptions be created with one command but can also listen on channels not yet created at subscription time (as long as they match the pattern).</p>
</div>
<div class="paragraph">
<p>At the low-level, <code class="notranslate">RedisConnection</code> offers the <code class="notranslate">subscribe</code> and <code class="notranslate">pSubscribe</code> methods that map the Redis commands for subscribing by channel or by pattern, respectively. Note that multiple channels or patterns can be used as arguments. To change the subscription of a connection or query whether it is listening, <code class="notranslate">RedisConnection</code> provides the <code class="notranslate">getSubscription</code> and <code class="notranslate">isSubscribed</code> methods.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Subscription commands in Spring Data Redis are blocking. That is, calling subscribe on a connection causes the current thread to block as it starts waiting for messages. The thread is released only if the subscription is canceled, which happens when another thread invokes <code class="notranslate">unsubscribe</code> or <code class="notranslate">pUnsubscribe</code> on the <strong>same</strong> connection. See “<a href="#redis:pubsub:subscribe:containers">Message Listener Containers</a>” (later in this document) for a solution to this problem. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>As mentioned earlier, once subscribed, a connection starts waiting for messages. Only commands that add new subscriptions, modify existing subscriptions, and cancel existing subscriptions are allowed. Invoking anything other than <code class="notranslate">subscribe</code>, <code class="notranslate">pSubscribe</code>, <code class="notranslate">unsubscribe</code>, or <code class="notranslate">pUnsubscribe</code> throws an exception.</p>
</div>
<div class="paragraph">
<p>In order to subscribe to messages, one needs to implement the <code class="notranslate">MessageListener</code> callback. Each time a new message arrives, the callback gets invoked and the user code gets run by the <code class="notranslate">onMessage</code> method. The interface gives access not only to the actual message but also to the channel it has been received through and the pattern (if any) used by the subscription to match the channel. This information lets the callee differentiate between various messages not just by content but also examining additional details.</p>
</div>
<div class="sect4">
<h5 id="redis:pubsub:subscribe:containers"><a class="anchor" href="#redis:pubsub:subscribe:containers"></a>Message Listener Containers</h5>
<div class="paragraph">
<p>Due to its blocking nature, low-level subscription is not attractive, as it requires connection and thread management for every single listener. To alleviate this problem, Spring Data offers <code class="notranslate">RedisMessageListenerContainer</code>, which does all the heavy lifting. If you are familiar with EJB and JMS, you should find the concepts familiar, as it is designed to be as close as possible to the support in Spring Framework and its message-driven POJOs (MDPs).</p>
</div>
<div class="paragraph">
<p><code class="notranslate">RedisMessageListenerContainer</code> acts as a message listener container. It is used to receive messages from a Redis channel and drive the <code class="notranslate">MessageListener</code> instances that are injected into it. The listener container is responsible for all threading of message reception and dispatches into the listener for processing. A message listener container is the intermediary between an MDP and a messaging provider and takes care of registering to receive messages, resource acquisition and release, exception conversion, and the like. This lets you as an application developer write the (possibly complex) business logic associated with receiving a message (and reacting to it) and delegates boilerplate Redis infrastructure concerns to the framework.</p>
</div>
<div class="paragraph">
<p>Furthermore, to minimize the application footprint, <code class="notranslate">RedisMessageListenerContainer</code> lets one connection and one thread be shared by multiple listeners even though they do not share a subscription. Thus, no matter how many listeners or channels an application tracks, the runtime cost remains the same throughout its lifetime. Moreover, the container allows runtime configuration changes so that you can add or remove listeners while an application is running without the need for a restart. Additionally, the container uses a lazy subscription approach, using a <code class="notranslate">RedisConnection</code> only when needed. If all the listeners are unsubscribed, cleanup is automatically performed, and the thread is released.</p>
</div>
<div class="paragraph">
<p>To help with the asynchronous nature of messages, the container requires a <code class="notranslate">java.util.concurrent.Executor</code> (or Spring’s <code class="notranslate">TaskExecutor</code>) for dispatching the messages. Depending on the load, the number of listeners, or the runtime environment, you should change or tweak the executor to better serve your needs. In particular, in managed environments (such as app servers), it is highly recommended to pick a proper <code class="notranslate">TaskExecutor</code> to take advantage of its runtime.</p>
</div>
</div>
<div class="sect4">
<h5 id="redis:pubsub:subscribe:adapter"><a class="anchor" href="#redis:pubsub:subscribe:adapter"></a>The MessageListenerAdapter</h5>
<div class="paragraph">
<p>The <code class="notranslate">MessageListenerAdapter</code> class is the final component in Spring’s asynchronous messaging support. In a nutshell, it lets you expose almost <strong>any</strong> class as a MDP (though there are some constraints).</p>
</div>
<div class="paragraph">
<p>Consider the following interface definition:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public interface MessageDelegate {
  void handleMessage(String message);
  void handleMessage(Map message); void handleMessage(byte[] message);
  void handleMessage(Serializable message);
  // pass the channel/pattern as well
  void handleMessage(Serializable message, String channel);
 }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Notice that, although the interface does not extend the <code class="notranslate">MessageListener</code> interface, it can still be used as a MDP by using the <code class="notranslate">MessageListenerAdapter</code> class. Notice also how the various message handling methods are strongly typed according to the <strong>contents</strong> of the various <code class="notranslate">Message</code> types that they can receive and handle. In addition, the channel or pattern to which a message is sent can be passed in to the method as the second argument of type <code class="notranslate">String</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class DefaultMessageDelegate implements MessageDelegate {
  // implementation elided for clarity...
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Notice how the above implementation of the <code class="notranslate">MessageDelegate</code> interface (the above <code class="notranslate">DefaultMessageDelegate</code> class) has <strong>no</strong> Redis dependencies at all. It truly is a POJO that we make into an MDP with the following configuration:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
 &lt;beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:redis="http://www.springframework.org/schema/redis"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd"&gt;

&lt;!-- the default ConnectionFactory --&gt;
&lt;redis:listener-container&gt;
  &lt;!-- the method attribute can be skipped as the default method name is "handleMessage" --&gt;
  &lt;redis:listener ref="listener" method="handleMessage" topic="chatroom" /&gt;
&lt;/redis:listener-container&gt;

&lt;bean id="listener" class="redisexample.DefaultMessageDelegate"/&gt;
 ...
&lt;beans&gt;</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> The listener topic can be either a channel (for example, <code class="notranslate">topic="chatroom"</code>) or a pattern (for example, <code class="notranslate">topic="*room"</code>) </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>The preceding example uses the Redis namespace to declare the message listener container and automatically register the POJOs as listeners. The full blown beans definition follows:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter"&gt;
  &lt;constructor-arg&gt;
    &lt;bean class="redisexample.DefaultMessageDelegate"/&gt;
  &lt;/constructor-arg&gt;
&lt;/bean&gt;

&lt;bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer"&gt;
  &lt;property name="connectionFactory" ref="connectionFactory"/&gt;
  &lt;property name="messageListeners"&gt;
    &lt;map&gt;
      &lt;entry key-ref="messageListener"&gt;
        &lt;bean class="org.springframework.data.redis.listener.ChannelTopic"&gt;
          &lt;constructor-arg value="chatroom"&gt;
        &lt;/bean&gt;
      &lt;/entry&gt;
    &lt;/map&gt;
  &lt;/property&gt;
&lt;/bean&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Each time a message is received, the adapter automatically and transparently performs translation (using the configured <code class="notranslate">RedisSerializer</code>) between the low-level format and the required object type. Any exception caused by the method invocation is caught and handled by the container (by default, exceptions get logged).</p>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="tx"><a class="anchor" href="#tx"></a>5.10. Redis Transactions</h3>
<div class="paragraph">
<p>Redis provides support for <a href="javascript:window.open('http://redis.io/topics/transactions');" target="_blank" rel="noopener noreferrer">transactions <i class="fa fa-external-link"></i></a> through the <code class="notranslate">multi</code>, <code class="notranslate">exec</code>, and <code class="notranslate">discard</code> commands. These operations are available on <code class="notranslate">RedisTemplate</code>. However, <code class="notranslate">RedisTemplate</code> is not guaranteed to execute all operations in the transaction with the same connection.</p>
</div>
<div class="paragraph">
<p>Spring Data Redis provides the <code class="notranslate">SessionCallback</code> interface for use when multiple operations need to be performed with the same <code class="notranslate">connection</code>, such as when using Redis transactions. The following example uses the <code class="notranslate">multi</code> method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">//execute a transaction
List&lt;Object&gt; txResults = redisTemplate.execute(new SessionCallback&lt;List&lt;Object&gt;&gt;() {
  public List&lt;Object&gt; execute(RedisOperations operations) throws DataAccessException {
    operations.multi();
    operations.opsForSet().add("key", "value1");

    // This will contain the results of all operations in the transaction
    return operations.exec();
  }
});
System.out.println("Number of items added to set: " + txResults.get(0));</code></pre>
</div>
</div>
<div class="paragraph">
<p><code class="notranslate">RedisTemplate</code> uses its value, hash key, and hash value serializers to deserialize all results of <code class="notranslate">exec</code> before returning. There is an additional <code class="notranslate">exec</code> method that lets you pass a custom serializer for transaction results.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> As of version 1.1, an important change has been made to the <code class="notranslate">exec</code> methods of <code class="notranslate">RedisConnection</code> and <code class="notranslate">RedisTemplate</code>. Previously, these methods returned the results of transactions directly from the connectors. This means that the data types often differed from those returned from the methods of <code class="notranslate">RedisConnection</code>. For example, <code class="notranslate">zAdd</code> returns a boolean indicating whether the element has been added to the sorted set. Most connectors return this value as a long, and Spring Data Redis performs the conversion. Another common difference is that most connectors return a status reply (usually the string, <code class="notranslate">OK</code>) for operations such as <code class="notranslate">set</code>. These replies are typically discarded by Spring Data Redis. Prior to 1.1, these conversions were not performed on the results of <code class="notranslate">exec</code>. Also, results were not deserialized in <code class="notranslate">RedisTemplate</code>, so they often included raw byte arrays. If this change breaks your application, set <code class="notranslate">convertPipelineAndTxResults</code> to <code class="notranslate">false</code> on your <code class="notranslate">RedisConnectionFactory</code> to disable this behavior. </td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="tx.spring"><a class="anchor" href="#tx.spring"></a>5.10.1. @Transactional Support</h4>
<div class="paragraph">
<p>By default, transaction Support is disabled and has to be explicitly enabled for each <code class="notranslate">RedisTemplate</code> in use by setting <code class="notranslate">setEnableTransactionSupport(true)</code>. Doing so forces binding the current <code class="notranslate">RedisConnection</code> to the current <code class="notranslate">Thread</code> that is triggering <code class="notranslate">MULTI</code>. If the transaction finishes without errors, <code class="notranslate">EXEC</code> is called. Otherwise <code class="notranslate">DISCARD</code> is called. Once in <code class="notranslate">MULTI</code>, <code class="notranslate">RedisConnection</code> queues write operations. All <code class="notranslate">readonly</code> operations, such as <code class="notranslate">KEYS</code>, are piped to a fresh (non-thread-bound) <code class="notranslate">RedisConnection</code>.</p>
</div>
<div class="paragraph">
<p>The following example shows how to configure transaction management:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 1. Configuration enabling Transaction Management
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Configuration
@EnableTransactionManagement                                 <i class="conum" data-value="1"></i><b>(1)</b>
public class RedisTxContextConfiguration {

  @Bean
  public StringRedisTemplate redisTemplate() {
    StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory());
    // explicitly enable transaction support
    template.setEnableTransactionSupport(true);              <i class="conum" data-value="2"></i><b>(2)</b>
    return template;
  }

  @Bean
  public RedisConnectionFactory redisConnectionFactory() {
    // jedis || Lettuce || srp || ...
  }

  @Bean
  public PlatformTransactionManager transactionManager() throws SQLException {
    return new DataSourceTransactionManager(dataSource());   <i class="conum" data-value="3"></i><b>(3)</b>
  }

  @Bean
  public DataSource dataSource() throws SQLException {
    // ...
  }
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Configures a Spring Context to enable <a href="javascript:window.open('https://docs.spring.io/spring/docs/4.3.22.RELEASE/spring-framework-reference/data-access.html#transaction-declarative');" target="_blank" rel="noopener noreferrer">declarative transaction management <i class="fa fa-external-link"></i></a>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Configures <code class="notranslate">RedisTemplate</code> to participate in transactions by binding connections to the current thread.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Transaction management requires a <code class="notranslate">PlatformTransactionManager</code>. Spring Data Redis does not ship with a <code class="notranslate">PlatformTransactionManager</code> implementation. Assuming your application uses JDBC, Spring Data Redis can participate in transactions by using existing transaction managers.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>The following examples each demonstrate a usage constraint:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 2. Usage Constraints
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">// must be performed on thread-bound connection
template.opsForValue().set("thing1", "thing2");

// read operation must be executed on a free (not transaction-aware) connection
template.keys("*");

// returns null as values set within a transaction are not visible
template.opsForValue().get("thing1");</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="pipeline"><a class="anchor" href="#pipeline"></a>5.11. Pipelining</h3>
<div class="paragraph">
<p>Redis provides support for <a href="javascript:window.open('http://redis.io/topics/pipelining');" target="_blank" rel="noopener noreferrer">pipelining <i class="fa fa-external-link"></i></a>, which involves sending multiple commands to the server without waiting for the replies and then reading the replies in a single step. Pipelining can improve performance when you need to send several commands in a row, such as adding many elements to the same List.</p>
</div>
<div class="paragraph">
<p>Spring Data Redis provides several <code class="notranslate">RedisTemplate</code> methods for executing commands in a pipeline. If you do not care about the results of the pipelined operations, you can use the standard <code class="notranslate">execute</code> method, passing <code class="notranslate">true</code> for the <code class="notranslate">pipeline</code> argument. The <code class="notranslate">executePipelined</code> methods run the provided <code class="notranslate">RedisCallback</code> or <code class="notranslate">SessionCallback</code> in a pipeline and return the results, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">//pop a specified number of items from a queue
List&lt;Object&gt; results = stringRedisTemplate.executePipelined(
  new RedisCallback&lt;Object&gt;() {
    public Object doInRedis(RedisConnection connection) throws DataAccessException {
      StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
      for(int i=0; i&lt; batchSize; i++) {
        stringRedisConn.rPop("myqueue");
      }
    return null;
  }
});</code></pre>
</div>
</div>
<div class="paragraph">
<p>The preceding example runs a bulk right pop of items from a queue in a pipeline. The <code class="notranslate">results</code> <code class="notranslate">List</code> contains all of the popped items. <code class="notranslate">RedisTemplate</code> uses its value, hash key, and hash value serializers to deserialize all results before returning, so the returned items in the preceding example are Strings. There are additional <code class="notranslate">executePipelined</code> methods that let you pass a custom serializer for pipelined results.</p>
</div>
 <div class="paragraph">
<p>Note that the value returned from the <code class="notranslate">RedisCallback</code> is required to be null, as this value is discarded in favor of returning the results of the pipelined commands.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> As of version 1.1, an important change has been made to the <code class="notranslate">exec</code> methods of <code class="notranslate">RedisConnection</code> and <code class="notranslate">RedisTemplate</code>. Previously, these methods returned the results of transactions directly from the connectors. This means that the data types often differed from those returned from the methods of <code class="notranslate">RedisConnection</code>. For example, <code class="notranslate">zAdd</code> returns a boolean indicating whether the element has been added to the sorted set. Most connectors return this value as a long, and Spring Data Redis performs the conversion. Another common difference is that most connectors return a status reply (usually the string, <code class="notranslate">OK</code>) for operations such as <code class="notranslate">set</code>. These replies are typically discarded by Spring Data Redis. Prior to 1.1, these conversions were not performed on the results of <code class="notranslate">exec</code>. Also, results were not deserialized in <code class="notranslate">RedisTemplate</code>, so they often included raw byte arrays. If this change breaks your application, set <code class="notranslate">convertPipelineAndTxResults</code> to <code class="notranslate">false</code> on your <code class="notranslate">RedisConnectionFactory</code> to disable this behavior. </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="scripting"><a class="anchor" href="#scripting"></a>5.12. Redis Scripting</h3>
<div class="paragraph">
<p>Redis versions 2.6 and higher provide support for execution of Lua scripts through the <a href="javascript:window.open('http://redis.io/commands/eval');" target="_blank" rel="noopener noreferrer">eval <i class="fa fa-external-link"></i></a> and <a href="javascript:window.open('http://redis.io/commands/evalsha');" target="_blank" rel="noopener noreferrer">evalsha <i class="fa fa-external-link"></i></a> commands. Spring Data Redis provides a high-level abstraction for script execution that handles serialization and automatically uses the Redis script cache.</p>
</div>
<div class="paragraph">
<p>Scripts can be run by calling the <code class="notranslate">execute</code> methods of <code class="notranslate">RedisTemplate</code>. It uses a configurable <code class="notranslate">ScriptExecutor</code> to run the provided script. By default, the <code class="notranslate">ScriptExecutor</code> takes care of serializing the provided keys and arguments and deserializing the script result. This is done through the key and value serializers of the template. There is an additional overload that lets you pass custom serializers for the script arguments and the result.</p>
</div>
<div class="paragraph">
<p>The default <code class="notranslate">ScriptExecutor</code> optimizes performance by retrieving the SHA1 of the script and attempting first to run <code class="notranslate">evalsha</code>, falling back to <code class="notranslate">eval</code> if the script is not yet present in the Redis script cache.</p>
</div>
<div class="paragraph">
<p>The following example runs a common “check-and-set” scenario by using a Lua script. This is an ideal use case for a Redis script, as it requires that running a set of commands atomically, and the behavior of one command is influenced by the result of another.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Bean
public RedisScript&lt;Boolean&gt; script() {
  DefaultRedisScript&lt;Boolean&gt; redisScript = new DefaultRedisScript&lt;Boolean&gt;();
  redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/scripts/checkandset.lua")));
  redisScript.setResultType(Boolean.class);
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class Example {
  @Autowired
  RedisScript&lt;Boolean&gt; script;
  public boolean checkAndSet(String expectedValue, String newValue) {
    return redisTemplate.execute(script, Collections.singletonList("key"), expectedValue, newValue);
  }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-lua" data-lang="lua">-- checkandset.lua
local current = redis.call('GET', KEYS[1])
if current == ARGV[1]
  then redis.call('SET', KEYS[1], ARGV[2])
  return true
end
return false</code></pre>
</div>
</div>
<div class="paragraph">
<p>The preceding code configures a <code class="notranslate">RedisScript</code> pointing to a file called <code class="notranslate">checkandset.lua</code>, which is expected to return a boolean value. The script <code class="notranslate">resultType</code> should be one of <code class="notranslate">Long</code>, <code class="notranslate">Boolean</code>, <code class="notranslate">List</code>, or a deserialized value type. It can also be <code class="notranslate">null</code> if the script returns a throw-away status (specifically, <code class="notranslate">OK</code>).</p>
</div>
<div class="admonitionblock tip">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td>
<td class="content"> It is ideal to configure a single instance of <code class="notranslate">DefaultRedisScript</code> in your application context to avoid re-calculation of the script’s SHA1 on every script execution. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>The <code class="notranslate">checkAndSet</code> method above then runs the scripts. Scripts can be run within a <code class="notranslate">SessionCallback</code> as part of a transaction or pipeline. See “<a href="#tx">Redis Transactions</a>” and “<a href="#pipeline">Pipelining</a>” for more information.</p>
</div>
<div class="paragraph">
<p>The scripting support provided by Spring Data Redis also lets you schedule Redis scripts for periodic execution by using the Spring Task and Scheduler abstractions. See the <a href="javascript:window.open('https://projects.spring.io/spring-framework/');" target="_blank" rel="noopener noreferrer">Spring Framework <i class="fa fa-external-link"></i></a> documentation for more details.</p>
</div>
</div>
<div class="sect2">
<h3 id="redis:support"><a class="anchor" href="#redis:support"></a>5.13. Support Classes</h3>
<div class="paragraph">
<p>Package <code class="notranslate">org.springframework.data.redis.support</code> offers various reusable components that rely on Redis as a backing store. Currently, the package contains various JDK-based interface implementations on top of Redis, such as <a href="javascript:window.open('https://download.oracle.com/javase/6/docs/api/java/util/concurrent/atomic/package-summary.html');" target="_blank" rel="noopener noreferrer">atomic <i class="fa fa-external-link"></i></a> counters and JDK <a href="javascript:window.open('https://download.oracle.com/javase/6/docs/api/java/util/Collection.html');" target="_blank" rel="noopener noreferrer">Collections <i class="fa fa-external-link"></i></a>.</p>
</div>
<div class="paragraph">
<p>The atomic counters make it easy to wrap Redis key incrementation while the collections allow easy management of Redis keys with minimal storage exposure or API leakage. In particular, the <code class="notranslate">RedisSet</code> and <code class="notranslate">RedisZSet</code> interfaces offer easy access to the set operations supported by Redis, such as <code class="notranslate">intersection</code> and <code class="notranslate">union</code>. <code class="notranslate">RedisList</code> implements the <code class="notranslate">List</code>, <code class="notranslate">Queue</code>, and <code class="notranslate">Deque</code> contracts (and their equivalent blocking siblings) on top of Redis, exposing the storage as a FIFO (First-In-First-Out), LIFO (Last-In-First-Out) or capped collection with minimal configuration. The following example shows the configuration for a bean that uses a <code class="notranslate">RedisList</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

  &lt;bean id="queue" class="org.springframework.data.redis.support.collections.DefaultRedisList"&gt;
    &lt;constructor-arg ref="redisTemplate"/&gt;
    &lt;constructor-arg value="queue-key"/&gt;
  &lt;/bean&gt;

&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The following example shows a Java configuration example for a <code class="notranslate">Deque</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class AnotherExample {

  // injected
  private Deque&lt;String&gt; queue;

  public void addTag(String tag) {
    queue.push(tag);
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>As shown in the preceding example, the consuming code is decoupled from the actual storage implementation. In fact, there is no indication that Redis is used underneath. This makes moving from development to production environments transparent and highly increases testability (the Redis implementation can be replaced with an in-memory one).</p>
</div>
<div class="sect3">
<h4 id="redis:support:cache-abstraction"><a class="anchor" href="#redis:support:cache-abstraction"></a>5.13.1. Support for the Spring Cache Abstraction</h4>
<div class="paragraph">
<p>Spring Redis provides an implementation for Spring <a href="javascript:window.open('https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html');" target="_blank" rel="noopener noreferrer">cache abstraction <i class="fa fa-external-link"></i></a> through the <code class="notranslate">org.springframework.data.redis.cache</code> package. To use Redis as a backing implementation, simply add <code class="notranslate">RedisCacheManager</code> to your configuration:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-xml" data-lang="xml">&lt;beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cache="http://www.springframework.org/schema/cache"
  xmlns:c="http://www.springframework.org/schema/c"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"&gt;

  &lt;!-- turn on declarative caching --&gt;
  &lt;cache:annotation-driven /&gt;

  &lt;!-- declare Redis Cache Manager --&gt;
  &lt;bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" c:template-ref="redisTemplate"/&gt;
&lt;/beans&gt;</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> By default <code class="notranslate">RedisCacheManager</code> will lazily initialize <code class="notranslate">RedisCache</code> whenever a <code class="notranslate">Cache</code> is requested. This can be changed by predefining a <code class="notranslate">Set</code> of cache names. </td>
</tr>
</tbody>
</table>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> By default <code class="notranslate">RedisCacheManager</code> will not participate in any ongoing transaction. Use <code class="notranslate">setTransactionAware</code> to enable transaction support. </td>
</tr>
</tbody>
</table>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> By default <code class="notranslate">RedisCacheManager</code> does not prefix keys for cache regions, which can lead to an unexpected growth of a <code class="notranslate">ZSET</code> used to maintain known keys. It’s highly recommended to enable the usage of prefixes in order to avoid this unexpected growth and potential key clashes using more than one cache region. </td>
</tr>
</tbody>
</table>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> By default <code class="notranslate">RedisCache</code> will not cache any <code class="notranslate">null</code> values as keys without a value get dropped by Redis itself. However you can explicitly enable <code class="notranslate">null</code> value caching via <code class="notranslate">RedisCacheManager</code> which will store <code class="notranslate">org.springframework.cache.support.NullValue</code> as a placeholder. </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="cluster"><a class="anchor" href="#cluster"></a>6. Redis Cluster</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Working with <a href="javascript:window.open('http://redis.io/topics/cluster-spec');" target="_blank" rel="noopener noreferrer">Redis Cluster <i class="fa fa-external-link"></i></a> requires Redis Server version 3.0+. See the <a href="javascript:window.open('http://redis.io/topics/cluster-tutorial');" target="_blank" rel="noopener noreferrer">Cluster Tutorial <i class="fa fa-external-link"></i></a> for more information.</p>
</div>
<div class="admonitionblock tip">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td>
<td class="content"> Redis Cluster is only supported by <a href="#redis:connectors:jedis">jedis</a> and <a href="#redis:connectors:lettuce">lettuce</a>. </td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_enabling_redis_cluster"><a class="anchor" href="#_enabling_redis_cluster"></a>6.1. Enabling Redis Cluster</h3>
<div class="paragraph">
<p>Cluster support is based on the same building blocks as non-clustered communication. <code class="notranslate">RedisClusterConnection</code>, an extension to <code class="notranslate">RedisConnection</code>, handles the communication with the Redis Cluster and translates errors into the Spring DAO exception hierarchy. <code class="notranslate">RedisClusterConnection</code> instances are created with the <code class="notranslate">RedisConnectionFactory</code>, which has to be set up with the associated <code class="notranslate">RedisClusterConfiguration</code>, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 3. Sample RedisConnectionFactory Configuration for Redis Cluster
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class ClusterConfigurationProperties {

    /*
     * spring.redis.cluster.nodes[0] = 127.0.0.1:7379
     * spring.redis.cluster.nodes[1] = 127.0.0.1:7380
     * ...
     */
    List&lt;String&gt; nodes;

    /**
     * Get initial collection of known cluster nodes in format {@code host:port}.
     *
     * @return
     */
    public List&lt;String&gt; getNodes() {
        return nodes;
    }

    public void setNodes(List&lt;String&gt; nodes) {
        this.nodes = nodes;
    }
}

@Configuration
public class AppConfig {

    /**
     * Type safe representation of application.properties
     */
    @Autowired ClusterConfigurationProperties clusterProperties;

    public @Bean RedisConnectionFactory connectionFactory() {

        return new JedisConnectionFactory(
            new RedisClusterConfiguration(clusterProperties.getNodes()));
    }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td>
<td class="content">
<div class="paragraph">
<p><code class="notranslate">RedisClusterConfiguration</code> can also be defined through <code class="notranslate">PropertySource</code> and has the following properties:</p>
</div>
<div class="ulist">
<div class="title">
Configuration Properties
</div>
<ul>
<li> <p><code class="notranslate">spring.redis.cluster.nodes</code>: Comma-delimited list of host:port pairs.</p> </li>
<li> <p><code class="notranslate">spring.redis.cluster.max-redirects</code>: Number of allowed cluster redirections.</p> </li>
</ul>
</div> </td>
</tr>
</tbody>
</table>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> The initial configuration points driver libraries to an initial set of cluster nodes. Changes resulting from live cluster reconfiguration are kept only in the native driver and are not written back to the configuration. </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_working_with_redis_cluster_connection"><a class="anchor" href="#_working_with_redis_cluster_connection"></a>6.2. Working With Redis Cluster Connection</h3>
<div class="paragraph">
<p>As mentioned earlier, Redis Cluster behaves differently from single-node Redis or even a Sentinel-monitored master-slave environment. This is because the automatic sharding maps a key to one of 16384 slots, which are distributed across the nodes. Therefore, commands that involve more than one key must assert all keys map to the exact same slot to avoid cross-slot execution errors. A single cluster node serves only a dedicated set of keys. Commands issued against one particular server return results only for those keys served by that server. As a simple example, consider the <code class="notranslate">KEYS</code> command. When issued to a server in a cluster environment, it returns only the keys served by the node the request is sent to and not necessarily all keys within the cluster. So, to get all keys in a cluster environment, you must read the keys from all the known master nodes.</p>
</div>
<div class="paragraph">
<p>While redirects for specific keys to the corresponding slot-serving node are handled by the driver libraries, higher-level functions, such as collecting information across nodes or sending commands to all nodes in the cluster, are covered by <code class="notranslate">RedisClusterConnection</code>. Picking up the keys example from earlier, this means that the <code class="notranslate">keys(pattern)</code> method picks up every master node in the cluster and simultaneously executes the <code class="notranslate">KEYS</code> command on every master node while picking up the results and returning the cumulated set of keys. To just request the keys of a single node <code class="notranslate">RedisClusterConnection</code> provides overloads for those methods (for example, <code class="notranslate">keys(node, pattern)</code>).</p>
</div>
<div class="paragraph">
<p>A <code class="notranslate">RedisClusterNode</code> can be obtained from <code class="notranslate">RedisClusterConnection.clusterGetNodes</code> or it can be constructed by using either the host and the port or the node Id.</p>
</div>
<div class="paragraph">
<p>The following example shows a set of commands being run across the cluster:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 4. Sample of Running Commands Across the Cluster
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text"><a href="javascript:window.open('https://docs.spring.io/cdn-cgi/l/email-protection');" class="__cf_email__" data-cfemail="97e5f2f3fee4baf4fbfed7a6a5a0b9a7b9a7b9a6" target="_blank" rel="noopener noreferrer">[email&nbsp;protected] <i class="fa fa-external-link"></i></a>:7379 &gt; cluster nodes

6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460                      <i class="conum" data-value="1"></i><b>(1)</b>
7bb78c... 127.0.0.1:7380 master - 0 1449730618304 2 connected 5461-10922       <i class="conum" data-value="2"></i><b>(2)</b>
164888... 127.0.0.1:7381 master - 0 1449730618304 3 connected 10923-16383      <i class="conum" data-value="3"></i><b>(3)</b>
b8b5ee... 127.0.0.1:7382 slave 6b38bb... 0 1449730618304 25 connected          <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">RedisClusterConnection connection = connectionFactory.getClusterConnnection();

connection.set("thing1", value);                                               <i class="conum" data-value="5"></i><b>(5)</b>
connection.set("thing2", value);                                               <i class="conum" data-value="6"></i><b>(6)</b>

connection.keys("*");                                                          <i class="conum" data-value="7"></i><b>(7)</b>

connection.keys(NODE_7379, "*");                                               <i class="conum" data-value="8"></i><b>(8)</b>
connection.keys(NODE_7380, "*");                                               <i class="conum" data-value="9"></i><b>(9)</b>
connection.keys(NODE_7381, "*");                                               <i class="conum" data-value="10"></i><b>(10)</b>
connection.keys(NODE_7382, "*");                                               <i class="conum" data-value="11"></i><b>(11)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Master node serving slots 0 to 5460 replicated to slave at 7382</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Master node serving slots 5461 to 10922</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Master node serving slots 10923 to 16383</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b class="notranslate">4</b></td>
<td>Slave node holding replicants of the master at 7379</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b class="notranslate">5</b></td>
<td>Request routed to node at 7381 serving slot 12182</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b class="notranslate">6</b></td>
<td>Request routed to node at 7379 serving slot 5061</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b class="notranslate">7</b></td>
<td>Request routed to nodes at 7379, 7380, 7381 → [thing1, thing2]</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b class="notranslate">8</b></td>
<td>Request routed to node at 7379 → [thing2]</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b class="notranslate">9</b></td>
<td>Request routed to node at 7380 → []</td>
</tr>
<tr>
<td><i class="conum" data-value="10"></i><b class="notranslate">10</b></td>
<td>Request routed to node at 7381 → [thing1]</td>
</tr>
<tr>
<td><i class="conum" data-value="11"></i><b class="notranslate">11</b></td>
<td>Request routed to node at 7382 → [thing2]</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>When all keys map to the same slot, the native driver library automatically serves cross-slot requests, such as <code class="notranslate">MGET</code>. However, once this is not the case, <code class="notranslate">RedisClusterConnection</code> executes multiple parallel <code class="notranslate">GET</code> commands against the slot-serving nodes and again returns an accumulated result. This is less performant than the single-slot execution and, therefore, should be used with care. If in doubt, consider pinning keys to the same slot by providing a prefix in curly brackets, such as <code class="notranslate">{my-prefix}.thing1</code> and <code class="notranslate">{my-prefix}.thing2</code>, which will both map to the same slot number. The following example shows cross-slot request handling:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 5. Sample of Cross-Slot Request Handling
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text"><a href="javascript:window.open('https://docs.spring.io/cdn-cgi/l/email-protection');" class="__cf_email__" data-cfemail="e29087868b91cf818e8ba2d3d0d5ccd2ccd2ccd3" target="_blank" rel="noopener noreferrer">[email&nbsp;protected] <i class="fa fa-external-link"></i></a>:7379 &gt; cluster nodes

6b38bb... 127.0.0.1:7379 master - 0 0 25 connected 0-5460                      <i class="conum" data-value="1"></i><b>(1)</b>
7bb...</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">RedisClusterConnection connection = connectionFactory.getClusterConnnection();

connection.set("thing1", value);           // slot: 12182
connection.set("{thing1}.thing2", value);  // slot: 12182
connection.set("thing2", value);           // slot:  5461

connection.mGet("thing1", "{thing1}.thing2");                                  <i class="conum" data-value="2"></i><b>(2)</b>

connection.mGet("thing1", "thing2");                                           <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Same Configuration as in the sample before.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Keys map to same slot → 127.0.0.1:7381 MGET thing1 {thing1}.thing2</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Keys map to different slots and get split up into single slot ones routed to the according nodes<br> → 127.0.0.1:7379 GET thing2<br> → 127.0.0.1:7381 GET thing1</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td>
<td class="content"> The preceding examples demonstrate the general strategy followed by Spring Data Redis. Be aware that some operations might require loading huge amounts of data into memory to compute the desired command. Additionally, not all cross-slot requests can safely be ported to multiple single slot requests and error if misused (for example, <code class="notranslate">PFCOUNT</code>). </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_working_with_code_redistemplate_code_and_code_clusteroperations_code"><a class="anchor" href="#_working_with_code_redistemplate_code_and_code_clusteroperations_code"></a>6.3. Working with <code class="notranslate">RedisTemplate</code> and <code class="notranslate">ClusterOperations</code></h3>
<div class="paragraph">
<p>See the <a href="#redis:template">Working with Objects through RedisTemplate</a> section for information about the general purpose, configuration, and usage of <code class="notranslate">RedisTemplate</code>.</p>
</div>
<div class="admonitionblock caution">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-caution" title="Caution"></i> </td>
<td class="content"> Be careful when setting up <code class="notranslate">RedisTemplate#keySerializer</code> using any of the Json <code class="notranslate">RedisSerializers</code>, as changing JSON structure has immediate influence on hash slot calculation. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p><code class="notranslate">RedisTemplate</code> provides access to cluster-specific operations through the <code class="notranslate">ClusterOperations</code> interface, which can be obtained from <code class="notranslate">RedisTemplate.opsForCluster()</code>. This lets you explicitly run commands on a single node within the cluster while retaining the serialization and deserialization features configured for the template. It also provides administrative commands (such as <code class="notranslate">CLUSTER MEET</code>) or more high-level operations (for example, resharding).</p>
</div>
<div class="paragraph">
<p>The following example shows how to access <code class="notranslate">RedisClusterConnection</code> with <code class="notranslate">RedisTemplate</code>:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 6. Accessing
<code class="notranslate">RedisClusterConnection</code> with
<code class="notranslate">RedisTemplate</code>
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">ClusterOperations clusterOps = redisTemplate.opsForCluster();
clusterOps.shutdown(NODE_7379);                                              <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Shut down node at 7379 and cross fingers there is a slave in place that can take over.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="redis.repositories"><a class="anchor" href="#redis.repositories"></a>7. Redis Repositories</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Working with Redis Repositories lets you seamlessly convert and store domain objects in Redis Hashes, apply custom mapping strategies, and use secondary indexes.</p>
</div>
<div class="admonitionblock important">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-important" title="Important"></i> </td>
<td class="content"> Redis Repositories require at least Redis Server version 2.8.0 and do not work with transactions. Make sure to use a <code class="notranslate">RedisTemplate</code> with <a href="#tx.spring">disabled transaction support</a>. </td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="redis.repositories.usage"><a class="anchor" href="#redis.repositories.usage"></a>7.1. Usage</h3>
<div class="paragraph">
<p>Spring Data Redis lets you easily implement domain entities, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 7. Sample Person Entity
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@RedisHash("people")
public class Person {

  @Id String id;
  String firstname;
  String lastname;
  Address address;
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>We have a pretty simple domain object here. Note that it has a <code class="notranslate">@RedisHash</code> annotation on its type and a property named <code class="notranslate">id</code> that is annotated with <code class="notranslate">org.springframework.data.annotation.Id</code>. Those two items are responsible for creating the actual key used to persist the hash.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Properties annotated with <code class="notranslate">@Id</code> as well as those named <code class="notranslate">id</code> are considered as the identifier properties. Those with the annotation are favored over others. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>To now actually have a component responsible for storage and retrieval, we need to define a repository interface, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 8. Basic Repository Interface To Persist Person Entities
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public interface PersonRepository extends CrudRepository&lt;Person, String&gt; {

}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>As our repository extends <code class="notranslate">CrudRepository</code>, it provides basic CRUD and finder operations. The thing we need in between to glue things together is the corresponding Spring configuration, shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 9. JavaConfig for Redis Repositories
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Configuration
@EnableRedisRepositories
public class ApplicationConfig {

  @Bean
  public RedisConnectionFactory connectionFactory() {
    return new JedisConnectionFactory();
  }

  @Bean
  public RedisTemplate&lt;?, ?&gt; redisTemplate() {

    RedisTemplate&lt;byte[], byte[]&gt; template = new RedisTemplate&lt;byte[], byte[]&gt;();
    return template;
  }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Given the preceding setup, we can inject <code class="notranslate">PersonRepository</code> into our components, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 10. Access to Person Entities
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Autowired PersonRepository repo;

public void basicCrudOperations() {

  Person rand = new Person("rand", "al'thor");
  rand.setAddress(new Address("emond's field", "andor"));

  repo.save(rand);                                         <i class="conum" data-value="1"></i><b>(1)</b>

  repo.findOne(rand.getId());                              <i class="conum" data-value="2"></i><b>(2)</b>

  repo.count();                                            <i class="conum" data-value="3"></i><b>(3)</b>

  repo.delete(rand);                                       <i class="conum" data-value="4"></i><b>(4)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Generates a new <code class="notranslate">id</code> if the current value is <code class="notranslate">null</code> or reuses an already set <code class="notranslate">id</code> value and stores properties of type <code class="notranslate">Person</code> inside the Redis Hash with a key that has a pattern of <code class="notranslate">keyspace:id</code> — in this case, it might be <code class="notranslate">people:5d67b7e1-8640-4475-beeb-c666fab4c0e5</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Uses the provided <code class="notranslate">id</code> to retrieve the object stored at <code class="notranslate">keyspace:id</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Counts the total number of entities available within the keyspace, <code class="notranslate">people</code>, defined by <code class="notranslate">@RedisHash</code> on <code class="notranslate">Person</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b class="notranslate">4</b></td>
<td>Removes the key for the given object from Redis.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.mapping"><a class="anchor" href="#redis.repositories.mapping"></a>7.2. Object-to-Hash Mapping</h3>
<div class="paragraph">
<p>The Redis Repository support persists Objects to Hashes. This requires an Object-to-Hash conversion which is done by a <code class="notranslate">RedisConverter</code>. The default implementation uses <code class="notranslate">Converter</code> for mapping property values to and from Redis native <code class="notranslate">byte[]</code>.</p>
</div>
<div class="paragraph">
<p>Given the <code class="notranslate">Person</code> type from the previous sections, the default mapping looks like the following:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">_class = org.example.Person                 <i class="conum" data-value="1"></i><b>(1)</b>
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand                            <i class="conum" data-value="2"></i><b>(2)</b>
lastname = al’thor
address.city = emond's field                <i class="conum" data-value="3"></i><b>(3)</b>
address.country = andor</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>The <code class="notranslate">_class</code> attribute is included on the root level as well as on any nested interface or abstract types.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Simple property values are mapped by path.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Properties of complex types are mapped by their dot path.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>The following table describes the default mapping rules:</p>
</div>
<table class="tableblock frame-all grid-all spread">
<caption class="title">
Table 4. Default Mapping Rules
</caption>
<colgroup>
<col style="width: 16.6666%;">
<col style="width: 33.3333%;">
<col style="width: 50.0001%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Sample</th>
<th class="tableblock halign-left valign-top">Mapped Value</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Simple Type<br> (for example, String)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">String firstname = "rand";</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">firstname = "rand"</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Complex Type<br> (for example, Address)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Address address = new Address("emond’s field");</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">address.city = "emond’s field"</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">List<br> of Simple Type</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">List&lt;String&gt; nicknames = asList("dragon reborn", "lews therin");</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">nicknames.[0] = "dragon reborn",<br> nicknames.[1] = "lews therin"</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Map<br> of Simple Type</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Map&lt;String, String&gt; atts = asMap({"eye-color", "grey"}, {"…</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">atts.[eye-color] = "grey",<br> atts.[hair-color] = "…</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">List<br> of Complex Type</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">List&lt;Address&gt; addresses = asList(new Address("em…</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">addresses.[0].city = "emond’s field",<br> addresses.[1].city = "…</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Map<br> of Complex Type</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Map&lt;String, Address&gt; addresses = asMap({"home", new Address("em…</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">addresses.[home].city = "emond’s field",<br> addresses.[work].city = "…</p></td>
</tr>
</tbody>
</table>
<div class="admonitionblock caution">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-caution" title="Caution"></i> </td>
<td class="content"> Due to the flat representation structure, Map keys need to be simple types, such as <code class="notranslate">String</code> or <code class="notranslate">Number</code>. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>Mapping behavior can be customized by registering the corresponding <code class="notranslate">Converter</code> in <code class="notranslate">RedisCustomConversions</code>. Those converters can take care of converting from and to a single <code class="notranslate">byte[]</code> as well as <code class="notranslate">Map&lt;String,byte[]&gt;</code>. The first one is suitable for (for example) converting a complex type to (for example) a binary JSON representation that still uses the default mappings hash structure. The second option offers full control over the resulting hash.</p>
</div>
<div class="admonitionblock warning">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-warning" title="Warning"></i> </td>
<td class="content"> Writing objects to a Redis hash deletes the content from the hash and re-creates the whole hash, so data that has not been mapped is lost. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>The following example shows two sample byte array converters:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 11. Sample byte[] Converters
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@WritingConverter
public class AddressToBytesConverter implements Converter&lt;Address, byte[]&gt; {

  private final Jackson2JsonRedisSerializer&lt;Address&gt; serializer;

  public AddressToBytesConverter() {

    serializer = new Jackson2JsonRedisSerializer&lt;Address&gt;(Address.class);
    serializer.setObjectMapper(new ObjectMapper());
  }

  @Override
  public byte[] convert(Address value) {
    return serializer.serialize(value);
  }
}

@ReadingConverter
public class BytesToAddressConverter implements Converter&lt;byte[], Address&gt; {

  private final Jackson2JsonRedisSerializer&lt;Address&gt; serializer;

  public BytesToAddressConverter() {

    serializer = new Jackson2JsonRedisSerializer&lt;Address&gt;(Address.class);
    serializer.setObjectMapper(new ObjectMapper());
  }

  @Override
  public Address convert(byte[] value) {
    return serializer.deserialize(value);
  }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Using the preceding byte array <code class="notranslate">Converter</code> produces output similar to the following:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address = { city : "emond's field", country : "andor" }</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>The following example shows two examples of <code class="notranslate">Map</code> converters:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 12. Sample Map&lt;String,byte[]&gt; Converters
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@WritingConverter
public class AddressToMapConverter implements Converter&lt;Address, Map&lt;String,byte[]&gt;&gt; {

  @Override
  public Map&lt;String,byte[]&gt; convert(Address source) {
    return singletonMap("ciudad", source.getCity().getBytes());
  }
}

@ReadingConverter
public class MapToAddressConverter implements Converter&lt;Address, Map&lt;String, byte[]&gt;&gt; {

  @Override
  public Address convert(Map&lt;String,byte[]&gt; source) {
    return new Address(new String(source.get("ciudad")));
  }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Using the preceding Map <code class="notranslate">Converter</code> produces output similar to the following:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
 <div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
ciudad = "emond's field"</code></pre>
</div>
</div>
</div>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Custom conversions have no effect on index resolution. <a href="#redis.repositories.indexes">Secondary Indexes</a> are still created, even for custom converted types. </td>
</tr>
</tbody>
</table>
</div>
<div class="sect3">
<h4 id="_customizing_type_mapping"><a class="anchor" href="#_customizing_type_mapping"></a>7.2.1. Customizing Type Mapping</h4>
<div class="paragraph">
<p>If you want to avoid writing the entire Java class name as type information and would rather like to use a key, you can use the <code class="notranslate">@TypeAlias</code> annotation on the entity class being persisted. If you need to customize the mapping even more, look at the <a href="javascript:window.open('https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/convert/TypeInformationMapper.html');" target="_blank" rel="noopener noreferrer"><code class="notranslate">TypeInformationMapper</code> <i class="fa fa-external-link"></i></a> interface. An instance of that interface can be configured at the <code class="notranslate">DefaultRedisTypeMapper</code>, which can be configured on <code class="notranslate">MappingRedisConverter</code>.</p>
</div>
<div class="paragraph">
<p>The following example shows how to define a type alias for an entity:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 13. Defining
<code class="notranslate">@TypeAlias</code> for an entity
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@TypeAlias("pers")
class Person {

}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>The resulting document contains <code class="notranslate">pers</code> as the value in a <code class="notranslate">_class</code> field.</p>
</div>
<div class="sect4">
<h5 id="_configuring_custom_type_mapping"><a class="anchor" href="#_configuring_custom_type_mapping"></a>Configuring Custom Type Mapping</h5>
<div class="paragraph">
<p>The following example demonstrates how to configure a custom <code class="notranslate">RedisTypeMapper</code> in <code class="notranslate">MappingRedisConverter</code>:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 14. Configuring a custom
<code class="notranslate">RedisTypeMapper</code> via Spring Java Config
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">class CustomRedisTypeMapper extends DefaultRedisTypeMapper {
  //implement custom type mapping here
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Configuration
class SampleRedisConfiguration {

  @Bean
  public MappingRedisConverter redisConverter(RedisMappingContext mappingContext,
        RedisCustomConversions customConversions, ReferenceResolver referenceResolver) {

    MappingRedisConverter mappingRedisConverter = new MappingRedisConverter(mappingContext, null, referenceResolver,
            customTypeMapper());

    mappingRedisConverter.setCustomConversions(customConversions);

    return mappingRedisConverter;
  }

  @Bean
  public RedisTypeMapper customTypeMapper() {
    return new CustomRedisTypeMapper();
  }
}</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.keyspaces"><a class="anchor" href="#redis.repositories.keyspaces"></a>7.3. Keyspaces</h3>
<div class="paragraph">
<p>Keyspaces define prefixes used to create the actual key for the Redis Hash. By default, the prefix is set to <code class="notranslate">getClass().getName()</code>. You can alter this default by setting <code class="notranslate">@RedisHash</code> on the aggregate root level or by setting up a programmatic configuration. However, the annotated keyspace supersedes any other configuration.</p>
</div>
<div class="paragraph">
<p>The following example shows how to set the keyspace configuration with the <code class="notranslate">@EnableRedisRepositories</code> annotation:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 15. Keyspace Setup via
<code class="notranslate">@EnableRedisRepositories</code>
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Configuration
@EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {

    @Override
    protected Iterable&lt;KeyspaceSettings&gt; initialConfiguration() {
      return Collections.singleton(new KeyspaceSettings(Person.class, "people"));
    }
  }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>The following example shows how to programmatically set the keyspace:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 16. Programmatic Keyspace setup
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Configuration
@EnableRedisRepositories
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  @Bean
  public RedisMappingContext keyValueMappingContext() {
    return new RedisMappingContext(
      new MappingConfiguration(
        new MyKeyspaceConfiguration(), new IndexConfiguration()));
  }

  public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {

    @Override
    protected Iterable&lt;KeyspaceSettings&gt; initialConfiguration() {
      return Collections.singleton(new KeyspaceSettings(Person.class, "people"));
    }
  }
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.indexes"><a class="anchor" href="#redis.repositories.indexes"></a>7.4. Secondary Indexes</h3>
<div class="paragraph">
<p><a href="javascript:window.open('http://redis.io/topics/indexes');" target="_blank" rel="noopener noreferrer">Secondary indexes <i class="fa fa-external-link"></i></a> are used to enable lookup operations based on native Redis structures. Values are written to the according indexes on every save and are removed when objects are deleted or <a href="#redis.repositories.expirations">expire</a>.</p>
</div>
<div class="sect3">
<h4 id="redis.repositories.indexes.simple"><a class="anchor" href="#redis.repositories.indexes.simple"></a>7.4.1. Simple Property Index</h4>
<div class="paragraph">
<p>Given the sample <code class="notranslate">Person</code> entity shown earlier, we can create an index for <code class="notranslate">firstname</code> by annotating the property with <code class="notranslate">@Indexed</code>, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 17. Annotation driven indexing
 </div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@RedisHash("people")
public class Person {

  @Id String id;
  @Indexed String firstname;
  String lastname;
  Address address;
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Indexes are built up for actual property values. Saving two Persons (for example, "rand" and "aviendha") results in setting up indexes similar to the following:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">SADD people:firstname:rand e2c7dcee-b8cd-4424-883e-736ce564363e
SADD people:firstname:aviendha a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>It is also possible to have indexes on nested elements. Assume <code class="notranslate">Address</code> has a <code class="notranslate">city</code> property that is annotated with <code class="notranslate">@Indexed</code>. In that case, once <code class="notranslate">person.address.city</code> is not <code class="notranslate">null</code>, we have Sets for each city, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">SADD people:address.city:tear e2c7dcee-b8cd-4424-883e-736ce564363e</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Furthermore, the programmatic setup lets you define indexes on map keys and list properties, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@RedisHash("people")
public class Person {

  // ... other properties omitted

  Map&lt;String,String&gt; attributes;      <i class="conum" data-value="1"></i><b>(1)</b>
  Map&lt;String Person&gt; relatives;       <i class="conum" data-value="2"></i><b>(2)</b>
  List&lt;Address&gt; addresses;            <i class="conum" data-value="3"></i><b>(3)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td><code class="notranslate">SADD people:attributes.map-key:map-value e2c7dcee-b8cd-4424-883e-736ce564363e</code></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td><code class="notranslate">SADD people:relatives.map-key.firstname:tam e2c7dcee-b8cd-4424-883e-736ce564363e</code></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td><code class="notranslate">SADD people:addresses.city:tear e2c7dcee-b8cd-4424-883e-736ce564363e</code></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="admonitionblock caution">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-caution" title="Caution"></i> </td>
<td class="content"> Indexes cannot be resolved on <a href="#redis.repositories.references">References</a>. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>As with keyspaces, you can configure indexes without needing to annotate the actual domain type, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 18. Index Setup with @EnableRedisRepositories
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Configuration
@EnableRedisRepositories(indexConfiguration = MyIndexConfiguration.class)
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  public static class MyIndexConfiguration extends IndexConfiguration {

    @Override
    protected Iterable&lt;IndexDefinition&gt; initialConfiguration() {
      return Collections.singleton(new SimpleIndexDefinition("people", "firstname"));
    }
  }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>Again, as with keyspaces, you can programmatically configure indexes, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 19. Programmatic Index setup
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@Configuration
@EnableRedisRepositories
public class ApplicationConfig {

  //... RedisConnectionFactory and RedisTemplate Bean definitions omitted

  @Bean
  public RedisMappingContext keyValueMappingContext() {
    return new RedisMappingContext(
      new MappingConfiguration(
        new KeyspaceConfiguration(), new MyIndexConfiguration()));
  }

  public static class MyIndexConfiguration extends IndexConfiguration {

    @Override
    protected Iterable&lt;IndexDefinition&gt; initialConfiguration() {
      return Collections.singleton(new SimpleIndexDefinition("people", "firstname"));
    }
  }
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="redis.repositories.indexes.geospatial"><a class="anchor" href="#redis.repositories.indexes.geospatial"></a>7.4.2. Geospatial Index</h4>
<div class="paragraph">
<p>Assume the <code class="notranslate">Address</code> type contains a <code class="notranslate">location</code> property of type <code class="notranslate">Point</code> that holds the geo coordinates of the particular address. By annotating the property with <code class="notranslate">@GeoIndexed</code>, Spring Data Redis adds those values by using Redis <code class="notranslate">GEO</code> commands, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@RedisHash("people")
public class Person {

  Address address;

  // ... other properties omitted
}

public class Address {

  @GeoIndexed Point location;

  // ... other properties omitted
}

public interface PersonRepository extends CrudRepository&lt;Person, String&gt; {

  List&lt;Person&gt; findByAddressLocationNear(Point point, Distance distance);     <i class="conum" data-value="1"></i><b>(1)</b>
  List&lt;Person&gt; findByAddressLocationWithin(Circle circle);                    <i class="conum" data-value="2"></i><b>(2)</b>
}

Person rand = new Person("rand", "al'thor");
rand.setAddress(new Address(new Point(13.361389D, 38.115556D)));

repository.save(rand);                                                        <i class="conum" data-value="3"></i><b>(3)</b>

repository.findByAddressLocationNear(new Point(15D, 37D), new Distance(200)); <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Query method declaration on a nested property, using <code class="notranslate">Point</code> and <code class="notranslate">Distance</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Query method declaration on a nested property, using <code class="notranslate">Circle</code> to search within.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td><code class="notranslate">GEOADD people:address:location 13.361389 38.115556 e2c7dcee-b8cd-4424-883e-736ce564363e</code></td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b class="notranslate">4</b></td>
<td><code class="notranslate">GEORADIUS people:address:location 15.0 37.0 200.0 km</code></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>In the preceding example the, longitude and latitude values are stored by using <code class="notranslate">GEOADD</code> that use the object’s <code class="notranslate">id</code> as the member’s name. The finder methods allow usage of <code class="notranslate">Circle</code> or <code class="notranslate">Point, Distance</code> combinations for querying those values.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> It is <strong>not</strong> possible to combine <code class="notranslate">near</code> and <code class="notranslate">within</code> with other criteria. </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.expirations"><a class="anchor" href="#redis.repositories.expirations"></a>7.5. Time To Live</h3>
<div class="paragraph">
<p>Objects stored in Redis may be valid only for a certain amount of time. This is especially useful for persisting short-lived objects in Redis without having to remove them manually when they reach their end of life. The expiration time in seconds can be set with <code class="notranslate">@RedisHash(timeToLive=…)</code> as well as by using <code class="notranslate">KeyspaceSettings</code> (see <a href="#redis.repositories.keyspaces">Keyspaces</a>).</p>
</div>
<div class="paragraph">
<p>More flexible expiration times can be set by using the <code class="notranslate">@TimeToLive</code> annotation on either a numeric property or a method. However, do not apply <code class="notranslate">@TimeToLive</code> on both a method and a property within the same class. The following example shows the <code class="notranslate">@TimeToLive</code> annotation on a property and on a method:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 20. Expirations
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public class TimeToLiveOnProperty {

  @Id
  private String id;

  @TimeToLive
  private Long expiration;
}

public class TimeToLiveOnMethod {

  @Id
  private String id;

  @TimeToLive
  public long getTimeToLive() {
  	return new Random().nextLong();
  }
}</code></pre>
</div>
</div>
</div>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Annotating a property explicitly with <code class="notranslate">@TimeToLive</code> reads back the actual <code class="notranslate">TTL</code> or <code class="notranslate">PTTL</code> value from Redis. -1 indicates that the object has no associated expiration. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>The repository implementation ensures subscription to <a href="javascript:window.open('http://redis.io/topics/notifications');" target="_blank" rel="noopener noreferrer">Redis keyspace notifications <i class="fa fa-external-link"></i></a> via <code class="notranslate">RedisMessageListenerContainer</code>.</p>
</div>
<div class="paragraph">
<p>When the expiration is set to a positive value, the corresponding <code class="notranslate">EXPIRE</code> command is executed. In addition to persisting the original, a phantom copy is persisted in Redis and set to expire five minutes after the original one. This is done to enable the Repository support to publish <code class="notranslate">RedisKeyExpiredEvent</code>, holding the expired value in Spring’s <code class="notranslate">ApplicationEventPublisher</code> whenever a key expires, even though the original values have already been removed. Expiry events are received on all connected applications that use Spring Data Redis repositories.</p>
</div>
<div class="paragraph">
<p>By default, the key expiry listener is disabled when initializing the application. The startup mode can be adjusted in <code class="notranslate">@EnableRedisRepositories</code> or <code class="notranslate">RedisKeyValueAdapter</code> to start the listener with the application or upon the first insert of an entity with a TTL. See <a href="javascript:window.open('https://docs.spring.io/spring-data/redis/docs/1.8.18.RELEASE/api/org/springframework/data/redis/core/RedisKeyValueAdapter.EnableKeyspaceEvents.html');" target="_blank" rel="noopener noreferrer"><code class="notranslate">EnableKeyspaceEvents</code> <i class="fa fa-external-link"></i></a> for possible values.</p>
</div>
<div class="paragraph">
<p>The <code class="notranslate">RedisKeyExpiredEvent</code> holds a copy of the expired domain object as well as the key.</p>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Delaying or disabling the expiry event listener startup impacts <code class="notranslate">RedisKeyExpiredEvent</code> publishing. A disabled event listener does not publish expiry events. A delayed startup can cause loss of events because of the delayed listener initialization. </td>
</tr>
</tbody>
</table>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> The keyspace notification message listener alters <code class="notranslate">notify-keyspace-events</code> settings in Redis, if those are not already set. Existing settings are not overridden, so you must set up those settings correctly (or leave them empty). Note that <code class="notranslate">CONFIG</code> is disabled on AWS ElastiCache, and enabling the listener leads to an error. </td>
</tr>
</tbody>
</table>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Redis Pub/Sub messages are not persistent. If a key expires while the application is down, the expiry event is not processed, which may lead to secondary indexes containing references to the expired object. </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.references"><a class="anchor" href="#redis.repositories.references"></a>7.6. Persisting References</h3>
<div class="paragraph">
<p>Marking properties with <code class="notranslate">@Reference</code> allows storing a simple key reference instead of copying values into the hash itself. On loading from Redis, references are resolved automatically and mapped back into the object, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 21. Sample Property Reference
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
mother = people:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56      <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Reference stores the whole key (<code class="notranslate">keyspace:id</code>) of the referenced object.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-warning" title="Warning"></i> </td>
<td class="content"> Referenced Objects are not persisted when the referencing object is saved. You must persist changes on referenced objects separately, since only the reference is stored. Indexes set on properties of referenced types are not resolved. </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.partial-updates"><a class="anchor" href="#redis.repositories.partial-updates"></a>7.7. Persisting Partial Updates</h3>
<div class="paragraph">
<p>In some cases, you need not load and rewrite the entire entity just to set a new value within it. A session timestamp for the last active time might be such a scenario where you want to alter one property. <code class="notranslate">PartialUpdate</code> lets you define <code class="notranslate">set</code> and <code class="notranslate">delete</code> actions on existing objects while taking care of updating potential expiration times of both the entity itself and index structures. The following example shows a partial update:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 22. Sample Partial Update
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">PartialUpdate&lt;Person&gt; update = new PartialUpdate&lt;Person&gt;("e2c7dcee", Person.class)
  .set("firstname", "mat")                                                           <i class="conum" data-value="1"></i><b>(1)</b>
  .set("address.city", "emond's field")                                              <i class="conum" data-value="2"></i><b>(2)</b>
  .del("age");                                                                       <i class="conum" data-value="3"></i><b>(3)</b>

template.update(update);

update = new PartialUpdate&lt;Person&gt;("e2c7dcee", Person.class)
  .set("address", new Address("caemlyn", "andor"))                                   <i class="conum" data-value="4"></i><b>(4)</b>
  .set("attributes", singletonMap("eye-color", "grey"));                             <i class="conum" data-value="5"></i><b>(5)</b>

template.update(update);

update = new PartialUpdate&lt;Person&gt;("e2c7dcee", Person.class)
  .refreshTtl(true);                                                                 <i class="conum" data-value="6"></i><b>(6)</b>
  .set("expiration", 1000);

template.update(update);</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Set the simple <code class="notranslate">firstname</code> property to <code class="notranslate">mat</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Set the simple 'address.city' property to 'emond’s field' without having to pass in the entire object. This does not work when a custom conversion is registered.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Remove the <code class="notranslate">age</code> property.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b class="notranslate">4</b></td>
<td>Set complex <code class="notranslate">address</code> property.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b class="notranslate">5</b></td>
<td>Set a map of values, which removes the previously existing map and replaces the values with the given ones.</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b class="notranslate">6</b></td>
<td>Automatically update the server expiration time when altering <a href="#redis.repositories.expirations">Time To Live</a>.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Updating complex objects as well as map (or other collection) structures requires further interaction with Redis to determine existing values, which means that rewriting the entire entity might be faster. </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.queries"><a class="anchor" href="#redis.repositories.queries"></a>7.8. Queries and Query Methods</h3>
<div class="paragraph">
<p>Query methods allow automatic derivation of simple finder queries from the method name, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 23. Sample Repository finder Method
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">public interface PersonRepository extends CrudRepository&lt;Person, String&gt; {

  List&lt;Person&gt; findByFirstname(String firstname);
}</code></pre>
</div>
</div>
</div>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Please make sure properties used in finder methods are set up for indexing. </td>
</tr>
</tbody>
</table>
</div>
<div class="admonitionblock note">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-note" title="Note"></i> </td>
<td class="content"> Query methods for Redis repositories support only queries for entities and collections of entities with paging. </td>
</tr>
</tbody>
</table>
</div>
<div class="paragraph">
<p>Using derived query methods might not always be sufficient to model the queries to execute. <code class="notranslate">RedisCallback</code> offers more control over the actual matching of index structures or even custom indexes. To do so, provide a <code class="notranslate">RedisCallback</code> that returns a single or <code class="notranslate">Iterable</code> set of <code class="notranslate">id</code> values, as shown in the following example:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 24. Sample finder using RedisCallback
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">String user = //...

List&lt;RedisSession&gt; sessionsByUser = template.find(new RedisCallback&lt;Set&lt;byte[]&gt;&gt;() {

  public Set&lt;byte[]&gt; doInRedis(RedisConnection connection) throws DataAccessException {
    return connection
      .sMembers("sessions:securityContext.authentication.principal.username:" + user);
  }}, RedisSession.class);</code></pre>
</div>
</div>
</div>
</div>
<div class="paragraph">
<p>The following table provides an overview of the keywords supported for Redis and what a method containing that keyword essentially translates to:</p>
</div>
<div class="exampleblock">
<div class="content">
<table class="tableblock frame-all grid-all">
<caption class="title">
Table 5. Supported keywords inside method names
</caption>
<colgroup>
<col>
<col>
<col>
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Keyword</th>
<th class="tableblock halign-left valign-top">Sample</th>
<th class="tableblock halign-left valign-top">Redis snippet</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">And</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">findByLastnameAndFirstname</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">SINTER …:firstname:rand …:lastname:al’thor</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Or</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">findByLastnameOrFirstname</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">SUNION …:firstname:rand …:lastname:al’thor</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Is,Equals</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">findByFirstname</code>,<code class="notranslate">findByFirstnameIs</code>,<code class="notranslate">findByFirstnameEquals</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">SINTER …:firstname:rand</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">Top,First</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="notranslate">findFirst10ByFirstname</code>,<code class="notranslate">findTop5ByFirstname</code></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.cluster"><a class="anchor" href="#redis.repositories.cluster"></a>7.9. Redis Repositories Running on a Cluster</h3>
<div class="paragraph">
<p>You can use the Redis repository support in a clustered Redis environment. See the “<a href="#cluster">Redis Cluster</a>” section for <code class="notranslate">ConnectionFactory</code> configuration details. Still, some additional configuration must be done, because the default key distribution spreads entities and secondary indexes through out the whole cluster and its slots.</p>
</div>
<div class="paragraph">
<p>The following table shows the details of data on a cluster (based on previous examples):</p>
</div>
<table class="tableblock frame-all grid-all">
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Key</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Slot</th>
<th class="tableblock halign-left valign-top">Node</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">people:e2c7dcee-b8cd-4424-883e-736ce564363e</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">id for hash</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">15171</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">127.0.0.1:7381</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">people:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">id for hash</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">7373</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">127.0.0.1:7380</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">people:firstname:rand</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">index</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">1700</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">127.0.0.1:7379</p></td>
</tr>
</tbody>
</table>
<div class="exampleblock">
<div class="content">
<div class="paragraph">
<p>Some commands (such as <code class="notranslate">SINTER</code> and <code class="notranslate">SUNION</code>) can only be processed on the server side when all involved keys map to the same slot. Otherwise, computation has to be done on client side. Therefore, it is useful to pin keyspaces to a single slot, which lets make use of Redis server side computation right away. The following table shows what happens when you do (note the change in the slot column and the port value in the node column):</p>
</div>
<table class="tableblock frame-all grid-all">
<colgroup>
<col>
<col>
<col>
<col>
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Key</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Slot</th>
<th class="tableblock halign-left valign-top">Node</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">{people}:e2c7dcee-b8cd-4424-883e-736ce564363e</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">id for hash</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2399</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">127.0.0.1:7379</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">{people}:a9d4b3a0-50d3-4538-a2fc-f7fc2581ee56</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">id for hash</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2399</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">127.0.0.1:7379</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">{people}:firstname:rand</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">index</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2399</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">127.0.0.1:7379</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tbody>
<tr>
<td class="icon"> <i class="fa icon-tip" title="Tip"></i> </td>
<td class="content"> Define and pin keyspaces by using <code class="notranslate">@RedisHash("{yourkeyspace}")</code> to specific slots when you use Redis cluster. </td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect2">
<h3 id="redis.repositories.cdi-integration"><a class="anchor" href="#redis.repositories.cdi-integration"></a>7.10. CDI Integration</h3>
<div class="paragraph">
<p>Instances of the repository interfaces are usually created by a container, for which Spring is the most natural choice when working with Spring Data. Spring offers sophisticated for creating bean instances. Spring Data Redis ships with a custom CDI extension that lets you use the repository abstraction in CDI environments. The extension is part of the JAR, so, to activate it, drop the Spring Data Redis JAR into your classpath.</p>
</div>
<div class="paragraph">
<p>You can then set up the infrastructure by implementing a CDI Producer for the <code class="notranslate">RedisConnectionFactory</code> and <code class="notranslate">RedisOperations</code>, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">class RedisOperationsProducer {

  @Produces
  RedisConnectionFactory redisConnectionFactory() {

    JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
    jedisConnectionFactory.setHostName("localhost");
    jedisConnectionFactory.setPort(6379);
    jedisConnectionFactory.afterPropertiesSet();

    return jedisConnectionFactory;
  }

  void disposeRedisConnectionFactory(@Disposes RedisConnectionFactory redisConnectionFactory) throws Exception {

    if (redisConnectionFactory instanceof DisposableBean) {
      ((DisposableBean) redisConnectionFactory).destroy();
    }
  }

  @Produces
  @ApplicationScoped
  RedisOperations&lt;byte[], byte[]&gt; redisOperationsProducer(RedisConnectionFactory redisConnectionFactory) {

    RedisTemplate&lt;byte[], byte[]&gt; template = new RedisTemplate&lt;byte[], byte[]&gt;();
    template.setConnectionFactory(redisConnectionFactory);
    template.afterPropertiesSet();

    return template;
  }

}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The necessary setup can vary, depending on your JavaEE environment.</p>
</div>
<div class="paragraph">
<p>The Spring Data Redis CDI extension picks up all available repositories as CDI beans and creates a proxy for a Spring Data repository whenever a bean of a repository type is requested by the container. Thus, obtaining an instance of a Spring Data repository is a matter of declaring an <code class="notranslate">@Injected</code> property, as shown in the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">class RepositoryClient {

  @Inject
  PersonRepository repository;

  public void businessMethod() {
    List&lt;Person&gt; people = repository.findAll();
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>A Redis Repository requires <code class="notranslate">RedisKeyValueAdapter</code> and <code class="notranslate">RedisKeyValueTemplate</code> instances. These beans are created and managed by the Spring Data CDI extension if no provided beans are found. You can, however, supply your own beans to configure the specific properties of <code class="notranslate">RedisKeyValueAdapter</code> and <code class="notranslate">RedisKeyValueTemplate</code>.</p>
</div>
</div>
<div class="sect2">
<h3 id="_redis_repositories_anatomy"><a class="anchor" href="#_redis_repositories_anatomy"></a>7.11. Redis Repositories Anatomy</h3>
<div class="paragraph">
<p>Redis as a store itself offers a very narrow low-level API leaving higher level functions, such as secondary indexes and query operations, up to the user.</p>
</div>
<div class="paragraph">
<p>This section provides a more detailed view of commands issued by the repository abstraction for a better understanding of potential performance implications.</p>
</div>
<div class="paragraph">
<p>Consider the following entity class as the starting point for all operations:</p>
</div>
<div class="exampleblock">
<div class="title">
Example 25. Example entity
</div>
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">@RedisHash("people")
public class Person {

  @Id String id;
  @Indexed String firstname;
  String lastname;
  Address hometown;
}

public class Address {

  @GeoIndexed Point location;
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_insert_new"><a class="anchor" href="#_insert_new"></a>7.11.1. Insert new</h4>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">repository.save(new Person("rand", "al'thor"));</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">HMSET "people:19315449-cda2-4f5c-b696-9cb8018fa1f9" "_class" "Person" "id" "19315449-cda2-4f5c-b696-9cb8018fa1f9" "firstname" "rand" "lastname" "al'thor" <i class="conum" data-value="1"></i><b>(1)</b>
SADD  "people" "19315449-cda2-4f5c-b696-9cb8018fa1f9"                           <i class="conum" data-value="2"></i><b>(2)</b>
SADD  "people:firstname:rand" "19315449-cda2-4f5c-b696-9cb8018fa1f9"            <i class="conum" data-value="3"></i><b>(3)</b>
SADD  "people:19315449-cda2-4f5c-b696-9cb8018fa1f9:idx" "people:firstname:rand" <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Save the flattened entry as hash.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Add the key of the hash written in &lt;1&gt; to the helper index of entities in the same keyspace.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Add the key of the hash written in &lt;2&gt; to the secondary index of firstnames with the properties value.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b class="notranslate">4</b></td>
<td>Add the index of &lt;3&gt; to the set of helper structures for entry to keep track of indexes to clean on delete/update.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_replace_existing"><a class="anchor" href="#_replace_existing"></a>7.11.2. Replace existing</h4>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">repository.save(new Person("e82908cf-e7d3-47c2-9eec-b4e0967ad0c9", "Dragon Reborn", "al'thor"));</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">DEL       "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9"                           <i class="conum" data-value="1"></i><b>(1)</b>
HMSET     "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" "_class" "Person" "id" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" "firstname" "Dragon Reborn" "lastname" "al'thor" <i class="conum" data-value="2"></i><b>(2)</b>
SADD      "people" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9"                         <i class="conum" data-value="3"></i><b>(3)</b>
SMEMBERS  "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9:idx"                       <i class="conum" data-value="4"></i><b>(4)</b>
TYPE      "people:firstname:rand"                                                 <i class="conum" data-value="5"></i><b>(5)</b>
SREM      "people:firstname:rand" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9"          <i class="conum" data-value="6"></i><b>(6)</b>
DEL       "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9:idx"                       <i class="conum" data-value="7"></i><b>(7)</b>
SADD      "people:firstname:Dragon Reborn" "e82908cf-e7d3-47c2-9eec-b4e0967ad0c9" <i class="conum" data-value="8"></i><b>(8)</b>
SADD      "people:e82908cf-e7d3-47c2-9eec-b4e0967ad0c9:idx" "people:firstname:Dragon Reborn" <i class="conum" data-value="9"></i><b>(9)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Remove the existing hash to avoid leftovers of hash keys potentially no longer present.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Save the flattened entry as hash.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b class="notranslate">3</b></td>
<td>Add the key of the hash written in &lt;1&gt; to the helper index of entities in the same keyspace.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b class="notranslate">4</b></td>
<td>Get existing index structures that might need to be updated.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b class="notranslate">5</b></td>
<td>Check if the index exists and what type it is (text, geo, …).</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b class="notranslate">6</b></td>
<td>Remove a potentially existing key from the index.</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b class="notranslate">7</b></td>
<td>Remove the helper holding index information.</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b class="notranslate">8</b></td>
<td>Add the key of the hash added in &lt;2&gt; to the secondary index of firstnames with the properties value.</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b class="notranslate">9</b></td>
<td>Add the index of &lt;6&gt; to the set of helper structures for entry to keep track of indexes to clean on delete/update.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_save_geo_data"><a class="anchor" href="#_save_geo_data"></a>7.11.3. Save Geo Data</h4>
<div class="paragraph">
<p>Geo indexes follow the same rules as normal text based ones but use geo structure to store values. Saving an entity that uses a Geo-indexed property results in the following commands:</p>
</div>
<div class="exampleblock">
<div class="content">
 <div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">GEOADD "people:hometown:location" "13.361389" "38.115556" "76900e94-b057-44bc-abcf-8126d51a621b"  <i class="conum" data-value="1"></i><b>(1)</b>
SADD   "people:76900e94-b057-44bc-abcf-8126d51a621b:idx" "people:hometown:location"               <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Add the key of the saved entry to the the geo index.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Keep track of the index structure.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_find_using_simple_index"><a class="anchor" href="#_find_using_simple_index"></a>7.11.4. Find using simple index</h4>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">repository.findByFirstname("egwene");</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">SINTER  "people:firstname:egwene"                     <i class="conum" data-value="1"></i><b>(1)</b>
HGETALL "people:d70091b5-0b9a-4c0a-9551-519e61bc9ef3" <i class="conum" data-value="2"></i><b>(2)</b>
HGETALL ...</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Fetch keys contained in the secondary index.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Fetch each key returned by &lt;1&gt; individually.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_find_using_geo_index"><a class="anchor" href="#_find_using_geo_index"></a>7.11.5. Find using Geo Index</h4>
<div class="exampleblock">
<div class="content">
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-java" data-lang="java">repository.findByHometownLocationNear(new Point(15, 37), new Distance(200, KILOMETERS));</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight notranslate"><code class="language-text" data-lang="text">GEORADIUS "people:hometown:location" "15.0" "37.0" "200.0" "km" <i class="conum" data-value="1"></i><b>(1)</b>
HGETALL   "people:76900e94-b057-44bc-abcf-8126d51a621b"         <i class="conum" data-value="2"></i><b>(2)</b>
HGETALL   ...</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tbody>
<tr>
<td><i class="conum" data-value="1"></i><b class="notranslate">1</b></td>
<td>Fetch keys contained in the secondary index.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b class="notranslate">2</b></td>
<td>Fetch each key returned by &lt;1&gt; individually.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<h2 id="appendixes" class="sect0"><a class="anchor" href="#appendixes"></a>Appendixes</h2>
<div class="openblock partintro">
<div class="content">
<h2 id="_appendix_document_structure" class="float">Appendix Document Structure</h2>
<div class="paragraph">
<p>The appendix contains various additional detail that complements the information in the rest of the reference documentation:</p>
</div>
<div class="ulist">
<ul>
<li> <p>“<a href="#appendix:schema">Schema</a>” defines the schemas provided by Spring Data Redis.</p> </li>
<li> <p>“<a href="#appendix:command-reference">Command Reference</a>” details which commands are supported by <code class="notranslate">RedisTemplate</code>.</p> </li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="appendix:schema"><a class="anchor" href="#appendix:schema"></a>Appendix A: Schema</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="javascript:window.open('http://www.springframework.org/schema/redis/spring-redis-1.0.xsd');" target="_blank" rel="noopener noreferrer">Spring Data Redis Schema (redis-namespace) <i class="fa fa-external-link"></i></a></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="appendix:command-reference"><a class="anchor" href="#appendix:command-reference"></a>Appendix B: Command Reference</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_supported_commands"><a class="anchor" href="#_supported_commands"></a>Supported Commands</h3>
<table class="tableblock frame-all grid-all" style="width: 50%;">
<caption class="title">
Table 6. Redis commands supported by
<code class="notranslate">RedisTemplate</code>
</caption>
<colgroup>
<col style="width: 66.6666%;">
<col style="width: 33.3334%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Command</th>
<th class="tableblock halign-center valign-top">Template Support</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">APPEND</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">AUTH</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BGREWRITEAOF</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BGSAVE</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BITCOUNT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BITOP</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BLPOP</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BRPOP</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">BRPOPLPUSH</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CLIENT KILL</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CLIENT GETNAME</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CLIENT LIST</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CLIENT SETNAME</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CLUSTER SLOTS</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">COMMAND</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">COMMAND COUNT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">COMMAND GETKEYS</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">COMMAND INFO</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CONFIG GET</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CONFIG RESETSTAT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CONFIG REWRITE</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">CONFIG SET</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DBSIZE</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DEBUG OBJECT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DEBUG SEGFAULT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DECR</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DECRBY</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DEL</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DISCARD</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">DUMP</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">ECHO</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">EVAL</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">EVALSHA</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">EXEC</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">EXISTS</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">EXPIRE</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">EXPIREAT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">FLUSHALL</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">FLUSHDB</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">GET</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">GETBIT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">GETRANGE</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">GETSET</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HDEL</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HEXISTS</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HGET</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HGETALL</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HINCRBY</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HINCRBYFLOAT</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HKEYS</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HLEN</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">HMGET</p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock">X</p></td>
</tr>
<tr>
</tr>
</tbody>
</table>
</div>
</div>
</div></div>
</div>
</section>
<div class="right-sidebar">
<div class="affix"><ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-6108808167664152" data-ad-slot="3102929424" data-ad-format="auto" data-full-width-responsive="true"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script></div>
</div>
</div>
<div class="ft">
<a href="#toolbar-title" id="anchorNavigationExGoTop"><i class="fa fa-arrow-up"></i></a>
<footer class="footer">
<div class="footer__container--normal">
<img alt="扫码关注公众号" title="扫码关注公众号" src="static/picture/qr-code.png" width="170" height="170">
<div class="footer__description--normal">
<p class="paragraph footer__author--normal">Docs4dev<sup class="super">&#xAE;</sup>
</p>
<p class="paragraph footer__quote&#45;&#45;normal">
如果你在使用过程中遇到任何问题，可以在 <a href="javascript:window.open('https://github.com/docs4dev/docs4dev-issues');" target="_blank" rel="noopener noreferrer">这里<i class="fa fa-external-link"></i></a> 提issue。
</p>
<div class="footer__main--normal">
<p class="paragraph footer__main__paragraph--normal copyright" style="color: #666 !important;">
<a href="javascript:window.open('https://beian.miit.gov.cn/');">
蜀ICP备14021783号-6
</a>
</p>
<p class="paragraph footer__main__paragraph--normal copyright" style="color: #666 !important;">
Copyright &#xA9; Docs4dev all
right reserved, powered by <a href="index2.html" target="_blank">Docs4dev</a></p>
</div>
</div>
</div>
<div class="box__issues">
</div>
</footer>
</div>
</div>
</div>
</div>
</div>
<script>
  var hasToc = true;
  /*  var downloadable = /!*false*!/ false;
    var editable = /!*false*!/ false;
    var code = /!*"spring-data-redis"*!/ false;
    var version = /!*"1.8.18.RELEASE"*!/ false;
    var type = /!*"reference"*!/ false;
    var lang = /!*"en"*!/ 'en';
    //edit link
    require(["gitbook", "jQuery"], function (gitbook, $) {
      gitbook.events.bind('start', function (e, config) {
        // Add edit toolbar to left
        var chapterId = /!*16333*!/ 0;
        if (downloadable) {
          gitbook.toolbar.createButton({
            icon: 'fa fa-download',
            text: '下载',
            onClick: function () {
              window.open('/download?code=' + code + '&version=' + version + '&type=' + type + '&lang=' + lang);
            }
          });
        }
        if (editable) {
          gitbook.toolbar.createButton({
            icon: 'fa fa-edit',
            text: '编辑此页',
            onClick: function () {
              window.open('/docs/edit?chapterId=' + chapterId + '&lang=' + lang);
            }
          });
        }
      });
    });*/
</script>
<script>
    var code = "spring-data-redis";
    var lang = "en";
  </script>

<script src="static/js/autocomplete-js.js"></script>
<script src="static/js/app.min.js"></script>
<script src="static/js/search.min.js"></script>
</body>
</html>
